Swim in Rising Water

HardGraphs · Minimax Path (Dijkstra)🧩 Shortest PathsLeetCode #778 ↗Updated

Problem

An n × n grid holds distinct elevations. At time t the water level is t, and you may move (instantly, 4-directionally) between cells whose elevations are both ≤ t. Return the earliest time at which you can get from the top-left cell to the bottom-right cell.
Input[[0,2],[1,3]]Output3
Input[[0,1,2,3,4],[24,23,22,21,5],
[12,13,14,15,16],[11,17,18,19,20],
[10,9,8,7,6]]
Output16

1 ≤ n ≤ 50  ·  0 ≤ grid[i][j] < n2  ·  all values distinct

Try it yourself

Java
class Solution {
    public int swimInWater(int[][] grid) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
The answer is the path that minimises its highest cell (a minimax path). Dijkstra works with “path cost = max elevation so far”, because extending a path can never lower that cost.
PatternShortest Paths

Instinct Weighted edges plus "minimum cost, time or effort to reach" - grab a min-heap and always expand the cheapest unsettled node next (Dijkstra).

Memory hack Pop the cheapest, skip the stale, relax the rest - Dijkstra is a ripple in a pond that touches near shores before far ones.

Open the Shortest Paths guide →

Approaches

Brute ForceTry Every Water Level + DFS

Time O(n4)Space O(n2)

For t = max(start, end), t+1, ... run a DFS that only enters cells with elevation ≤ t; the first t that reaches the corner is the answer. Up to n2 levels × O(n2) DFS.

Java
public int swimInWater(int[][] grid) {
    int n = grid.length;
    for (int t = Math.max(grid[0][0], grid[n - 1][n - 1]); ; t++)
        if (reach(grid, 0, 0, t, new boolean[n][n])) return t;
}
private boolean reach(int[][] g, int r, int c, int t, boolean[][] seen) {
    int n = g.length;
    if (r < 0 || c < 0 || r >= n || c >= n || seen[r][c] || g[r][c] > t) return false;
    if (r == n - 1 && c == n - 1) return true;
    seen[r][c] = true;
    return reach(g, r + 1, c, t, seen) || reach(g, r - 1, c, t, seen)
        || reach(g, r, c + 1, t, seen) || reach(g, r, c - 1, t, seen);
}

Obviously correct, but repeats a full flood fill for every level.

At a glance

LevelApproachTimeSpace
Brute ForceTry Every Water Level + DFSO(n4)O(n2)
BetterBinary Search on Time + DFSO(n2 log n)O(n2)
OptimalDijkstra (Min-Heap on Max Elevation)O(n2 log n)O(n2)
Common pitfalls
  • Starting the search below grid[0][0] or grid[n-1][n-1].
  • Summing elevations (shortest path) instead of taking the max along the path.
  • Returning when a cell is pushed rather than when it is popped.
Interview tips
  • Recognise “minimise the worst step” as a minimax / bottleneck path.
  • Mention the monotonic predicate ⇒ binary search on the answer.
  • Compare with Path With Minimum Effort — same Dijkstra template.
Pattern
Minimax path / Dijkstra with max → minimise the largest weight seen along a path in a grid or graph.
My notes saved in this browser