778. Swim in Rising Water
You are given an n x n
integer matrix grid
where each value grid[i][j]
represents the elevation at that point (i, j)
.
The rain starts to fall. At time t
, the depth of the water everywhere is t
. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t
. You can swim infinite distances in zero time. Of course, you must stay within the boundaries of the grid during your swim.
Return the least time until you can reach the bottom right square (n - 1, n - 1)
if you start at the top left square (0, 0)
.
Example 1:
Input: grid = [[0,2],[1,3]]
Output:
Explanation:
At time , you are in grid location (0, 0)
.
You cannot go anywhere else because 4-directionally adjacent neighbors have a higher elevation than t = 0
.
You cannot reach point (1, 1)
until time .
When the depth of water is , we can swim anywhere inside the grid.
Example 2:
Input: grid = [[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]]
Output:
Explanation: The final route is shown.
We need to wait until time so that (0, 0)
and (4, 4)
are connected.
Constraints:
n == grid.length
n == grid[i].length
-
grid[i][j]
- Each value
grid[i][j]
is unique.
Solution
Brute Force
In simpler terms, this problem is asking us what's the minimum value of such that you can travel from the top-left square to the bottom-right square by only travelling between adjacent cells that never exceed an elevation of . To solve this problem, we can try all values of from to . For each value of , we can run a BFS/flood fill algorithm to check the connectivity between the top-left and bottom-right squares. It's important that in this algorithm, we only traverse squares with elevations that don't exceed .
Since there are values of to try and our BFS/flood fill algorithm runs in , this gives us a time complexity of .
Full Solution
Let's denote a value as good if the bottom-right square is reachable from the top-left square only through squares with elevations not exceeding . Let's denote the minimum good value as . We can observe that all values such that are never good and that all values such that are all good. What does this mean for us? It means we can binary search it since our binary search condition is satisfied.
Each binary search iteration, we'll use the same BFS/flood fill algorithm to check if a specific value of is good.
Time Complexity
Note that so .
We have binary search iterations and each iteration uses a BFS/flood fill algorithm which runs in . Thus, our final time complexity is .
Time Complexity:
Space Complexity
Our BFS/flood fill algorithm will take space so our space complexity is .
Space Complexity:
C++ Solution
class Solution {
const vector<int> deltaRow = {-1, 0, 1, 0};
const vector<int> deltaCol = {0, 1, 0, -1};
bool isEndReachable(vector<vector<int>>& grid, int t) {
if (grid[0][0] > t) { // starting elevation can't exceed t
return false;
}
int n = grid.size();
vector<vector<bool>> vis(n, vector<bool>(n));
queue<vector<int>> q;
q.push({0, 0}); // starting cell
vis[0][0] = true;
while (!q.empty()) {
vector<int> cur = q.front();
q.pop();
int curRow = cur[0];
int curCol = cur[1];
for (int i = 0; i < 4; i++) {
int newRow = curRow + deltaRow[i];
int newCol = curCol + deltaCol[i];
if (newRow < 0 || newRow >= n || newCol < 0 || newCol >= n) { // outside of boundary
continue;
}
if (vis[newRow][newCol]) { // visited node before
continue;
}
if (grid[newRow][newCol] > t) { // check if cell can be traversed
continue;
}
vis[newRow][newCol] = true;
q.push({newRow, newCol});
}
}
return vis[n - 1][n - 1];
}
public:
int swimInWater(vector<vector<int>>& grid) {
int n = grid.size();
int low = -1; // all values smaller or equal to low are not good
int high = n * n; // all values greater or equal to high are good
int mid = (low + high) / 2;
while (low + 1 < high) {
if (isEndReachable(grid, mid)) {
high = mid;
} else {
low = mid;
}
mid = (low + high) / 2;
}
return high;
}
};
Java Solution
class Solution {
static int[] deltaRow = {-1, 0, 1, 0};
static int[] deltaCol = {0, 1, 0, -1};
boolean isEndReachable(int[][] grid, int t) {
if (grid[0][0] > t) { // starting elevation can't exceed t
return false;
}
int n = grid.length;
boolean[][] vis = new boolean[n][n];
Queue<int[]> q = new LinkedList<int[]>();
int[] start = {0, 0}; // starting cell
q.add(start);
vis[0][0] = true;
while (!q.isEmpty()) {
int[] cur = q.poll();
int curRow = cur[0];
int curCol = cur[1];
for (int i = 0; i < 4; i++) {
int newRow = curRow + deltaRow[i];
int newCol = curCol + deltaCol[i];
if (newRow < 0 || newRow >= n || newCol < 0 || newCol >= n) { // outside of boundary
continue;
}
if (vis[newRow][newCol]) { // visited node before
continue;
}
if (grid[newRow][newCol] > t) { // check if cell can be traversed
continue;
}
vis[newRow][newCol] = true;
int[] destination = {newRow, newCol};
q.add(destination);
}
}
return vis[n - 1][n - 1];
}
public int swimInWater(int[][] grid) {
int n = grid.length;
int low = -1; // all values smaller or equal to low are not good
int high = n * n; // all values greater or equal to high are good
int mid = (low + high) / 2;
while (low + 1 < high) {
if (isEndReachable(grid, mid)) {
high = mid;
} else {
low = mid;
}
mid = (low + high) / 2;
}
return high;
}
}
Python Solution
class Solution: def swimInWater(self, grid: List[List[int]]) -> int: deltaRow = [-1, 0, 1, 0] deltaCol = [0, 1, 0, -1] def isEndReachable(grid, t): if grid[0][0] > t: # starting elevation can't exceed t return False n = len(grid) vis = [[False] * n for a in range(n)] q = [(0, 0)] # starting cell vis[0][0] = True while len(q): (curRow, curCol) = q.pop() for i in range(4): newRow = curRow + deltaRow[i] newCol = curCol + deltaCol[i] if newRow < 0 or newRow >= n or newCol < 0 or newCol >= n: # outside of boundary continue if vis[newRow][newCol]: # visited node before continue if grid[newRow][newCol] > t: # check if cell can be traversed continue vis[newRow][newCol] = True q.append([newRow, newCol]) return vis[n - 1][n - 1] n = len(grid) low = -1 # all values smaller or equal to low are not good high = n * n # all values greater or equal to high are good mid = (low + high) // 2 while low + 1 < high: if isEndReachable(grid, mid): high = mid else: low = mid mid = (low + high) // 2 return high
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorWhat's the relationship between a tree and a graph?
Recommended Readings
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!