[LeetCode] 778. Swim in Rising Water
On an N x N grid
, each square grid[i][j]
represents the elevation at that point (i,j)
.
Now 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 distance in zero time. Of course, you must stay within the boundaries of the grid during your swim.
You start at the top left square (0, 0)
. What is the least time until you can reach the bottom right square (N-1, N-1)
?
Example 1:
Input: [[0,2],[1,3]] Output: 3 Explanation: At time0
, 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 time3
. When the depth of water is3
, we can swim anywhere inside the grid.
Example 2:
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]] Output: 16 Explanation: 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 The final route is marked in bold. We need to wait until time 16 so that (0, 0) and (4, 4) are connected.
Note:
2 <= N <= 50
.- grid[i][j] is a permutation of [0, ..., N*N - 1].
在一个 N x N 的坐标方格 grid 中,每一个方格的值 grid[i][j] 表示在位置 (i,j) 的平台高度。
现在开始下雨了。当时间为 t 时,此时雨水导致水池中任意位置的水位为 t 。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。
你从坐标方格的左上平台 (0,0) 出发。最少耗时多久你才能到达坐标方格的右下平台 (N-1, N-1)?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/swim-in-rising-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题非常好,有好几种类似的做法可以解决。分别是二分法,Dijkstra。
首先是二分法,这里我引用一个官方的图示帮助理解。
注意题目中的重要信息:假定你可以 瞬间移动 无限距离,游动不耗时。当前这个问题就转换成为:找一个时刻 t,使得这个二维网格上数值 小于等于 t 的部分,存在一条从左上角到右下角的路径。即:经过了时间 t 以后,可以瞬间从左上角(坐标 [0, 0])游到右下角(坐标 [N - 1, N - 1])。所以按照图示画的,在某些时间点,matrix 会被淹没到一个程度,使得你可以从左上角那个点移动到右下角那个点。题目要我们找的就是这个最小的,满足条件的时间点。因为时间的范围是 0 - N, N 又正好是这个 matrix 的边长,所以二分的范围就可以被划定为0 - N * N - 1。我们在这个范围做二分,每做一次二分我们就做一次 BFS/DFS 遍历,看看是否存在一条路径可以从左上角走到右下角,如果找到则移动 right 指针,看看是否能找到一个耗时更小的时间点满足条件;否则就移动 left 指针,说明当前得到的时间点 mid 是无法满足条件的。
时间O(n^2 * logn)
空间O(n^2)
Java实现 - BFS
1 class Solution { 2 private int N; 3 public static final int[][] dirs = { { 0, 1 }, { 0, -1 }, { -1, 0 }, { 1, 0 } }; 4 5 public int swimInWater(int[][] grid) { 6 this.N = grid.length; 7 int left = 0; 8 int right = N * N - 1; 9 while (left < right) { 10 int mid = left + (right - left) / 2; 11 if (grid[0][0] <= mid && bfs(grid, mid)) { 12 right = mid; 13 } else { 14 left = mid + 1; 15 } 16 } 17 return left; 18 } 19 20 private boolean bfs(int[][] grid, int threshold) { 21 Queue<int[]> queue = new LinkedList<>(); 22 queue.offer(new int[] { 0, 0 }); 23 boolean[][] visited = new boolean[N][N]; 24 visited[0][0] = true; 25 26 while (!queue.isEmpty()) { 27 int[] front = queue.poll(); 28 int x = front[0]; 29 int y = front[1]; 30 for (int[] dir : dirs) { 31 int newX = x + dir[0]; 32 int newY = y + dir[1]; 33 if (inArea(newX, newY) && !visited[newX][newY] && grid[newX][newY] <= threshold) { 34 if (newX == N - 1 && newY == N - 1) { 35 return true; 36 } 37 queue.offer(new int[] { newX, newY }); 38 visited[newX][newY] = true; 39 } 40 } 41 } 42 return false; 43 } 44 45 private boolean inArea(int x, int y) { 46 return x >= 0 && x < N && y >= 0 && y < N; 47 } 48 }
Java实现 - DFS
1 class Solution { 2 private int N; 3 public static final int[][] dirs = { { 0, 1 }, { 0, -1 }, { -1, 0 }, { 1, 0 } }; 4 5 public int swimInWater(int[][] grid) { 6 this.N = grid.length; 7 int left = 0; 8 int right = N * N - 1; 9 while (left < right) { 10 int mid = left + (right - left) / 2; 11 boolean[][] visited = new boolean[N][N]; 12 if (grid[0][0] <= mid && dfs(grid, 0, 0, visited, mid)) { 13 right = mid; 14 } else { 15 left = mid + 1; 16 } 17 } 18 return left; 19 } 20 21 private boolean dfs(int[][] grid, int x, int y, boolean[][] visited, int threshold) { 22 visited[x][y] = true; 23 for (int[] dir : dirs) { 24 int newX = x + dir[0]; 25 int newY = y + dir[1]; 26 if (inArea(newX, newY) && !visited[newX][newY] && grid[newX][newY] <= threshold) { 27 if (newX == N - 1 && newY == N - 1) { 28 return true; 29 } 30 if (dfs(grid, newX, newY, visited, threshold)) { 31 return true; 32 } 33 } 34 } 35 return false; 36 } 37 38 private boolean inArea(int x, int y) { 39 return x >= 0 && x < N && y >= 0 && y < N; 40 } 41 }
这道题还有一个 Dijkstra 算法的思路,时间空间复杂度跟二分法一样。因为这道题的 matrix 里面任意两个点之间连接的边的权重都是正数,而且我们找的是权重最小的路径,所以可以使用 Dijkstra 算法。如果你明白 Dijkstra 算法,代码就很好理解了。
时间O(n^2 * logn)
空间O(n^2)
Java实现
1 class Solution { 2 public int swimInWater(int[][] grid) { 3 PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (grid[a[0]][a[1]] - grid[b[0]][b[1]])); 4 pq.offer(new int[] { 0, 0 }); 5 int level = 0; 6 int n = grid.length; 7 int[][] dirs = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } }; 8 boolean[][] isVisited = new boolean[n][n]; 9 while (!pq.isEmpty()) { 10 int[] top = pq.poll(); 11 level = Math.max(level, grid[top[0]][top[1]]); 12 if (top[0] == n - 1 && top[1] == n - 1) { 13 break; 14 } 15 for (int[] dir : dirs) { 16 int x = top[0] + dir[0]; 17 int y = top[1] + dir[1]; 18 if (!(x < 0 || x > n - 1 || y < 0 || y > n - 1) && !isVisited[x][y]) { 19 isVisited[top[0]][top[1]] = true; 20 pq.offer(new int[] { x, y }); 21 } 22 } 23 } 24 return level; 25 } 26 }