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].
我的思路有点像最小生成树的prim算法。使用一个优先级队列。先压入元素0,然后pop出队首,找元素0的相邻元素,再压入队列。每次从队列中pop出的都是当前能连通到的最小元素。一直到能连通到右下角目标元素就结束,在这个过程中pop出的元素(即走的路径元素)中最大的那个就是result。
package leetcode;
import java.util.PriorityQueue;
public class Swim_in_Rising_Water_778 {
public int swimInWater(int[][] grid) {
int n=grid.length;
int[][] direction=new int[][]{
{-1,0},{1,0},{0,-1},{0,1}//上下左右
};
boolean[][] visited=new boolean[n][n];
PriorityQueue<Coordinate> pq=new PriorityQueue<Coordinate>((a,b) -> a.val-b.val);
pq.offer(new Coordinate(0, 0, grid[0][0]));
int max=0;
while(!pq.isEmpty()){
Coordinate c=pq.poll();
visited[c.x][c.y]=true;
max=Math.max(max, c.val);
if(c.x==n-1&&c.y==n-1){
break;
}
for(int i=0;i<4;i++){
int newX=c.x+direction[i][0];
int newY=c.y+direction[i][1];
if(newX<0||newX>=n||newY<0||newY>=n||visited[newX][newY]==true){
continue;
}
pq.offer(new Coordinate(newX, newY, grid[newX][newY]));
}
}
return max;
}
class Coordinate{
int x;
int y;
int val;
public Coordinate(int x,int y,int val){
this.x=x;
this.y=y;
this.val=val;
}
}
}
这道题有solutions,其中方法1的思路跟我的一样。
https://leetcode.com/problems/swim-in-rising-water/solution/
Approach #1: Heap [Accepted]
Intuition and Algorithm
保持一个优先级队列,队列中保存我们下一步可以去的方格。 我们下一步总会去“是访问过的结点的相邻4个方向的结点中最小的那个”。
当我们达到目标右下角时,迄今为止访问最大的数字就是答案。
class Solution {
public int swimInWater(int[][] grid) {
int N = grid.length;
Set<Integer> seen = new HashSet();
PriorityQueue<Integer> pq = new PriorityQueue<Integer>((k1, k2) ->
grid[k1 / N][k1 % N] - grid[k2 / N][k2 % N]);
pq.offer(0);
int ans = 0;
int[] dr = new int[]{1, -1, 0, 0};
int[] dc = new int[]{0, 0, 1, -1};
while (!pq.isEmpty()) {
int k = pq.poll();
int r = k / N, c = k % N;
ans = Math.max(ans, grid[r][c]);
if (r == N-1 && c == N-1) return ans;
for (int i = 0; i < 4; ++i) {
int cr = r + dr[i], cc = c + dc[i];
int ck = cr * N + cc;
if (0 <= cr && cr < N && 0 <= cc && cc < N && !seen.contains(ck)) {
pq.offer(ck);
seen.add(ck);
}
}
}
throw null;
}
}
Complexity Analysis
Time Complexity: O(N^2 \log N)O(N2logN). We may expand O(N^2)O(N2) nodes, and each one requires O(\log N)O(logN) time to perform the heap operations.
Space Complexity: O(N^2)O(N2), the maximum size of the heap.
Approach #2: Binary Search and DFS [Accepted]
Intuition and Algorithm
“是否能游”是一个随时间变化的单调函数,我们可以对这个函数进行二分查找,寻找“能游”的最小 T
。
假设正确的时间是 T
。为了检查是否能游,我们执行一个简单的深度优先搜索,检查在 T
时刻 是否能搜索到目标结点。
class Solution {
public int swimInWater(int[][] grid) {
int N = grid.length;
int lo = grid[0][0], hi = N * N; //根据题目最下行: grid[i][j]<=(N*N - 1)
while (lo < hi) {
int mi = lo + (hi - lo) / 2;
if (!possible(mi, grid)) {
lo = mi + 1;
} else {
hi = mi;
}
}
return lo;
}
public boolean possible(int T, int[][] grid) {
int N = grid.length;
Set<Integer> seen = new HashSet();
seen.add(0);
int[] dr = new int[]{1, -1, 0, 0};
int[] dc = new int[]{0, 0, 1, -1};
Stack<Integer> stack = new Stack();
stack.add(0);
while (!stack.empty()) {
int k = stack.pop();
int r = k / N, c = k % N;
if (r == N-1 && c == N-1) return true;
for (int i = 0; i < 4; ++i) {
int cr = r + dr[i], cc = c + dc[i];
int ck = cr * N + cc;
if (0 <= cr && cr < N && 0 <= cc && cc < N
&& !seen.contains(ck) && grid[cr][cc] <= T) {
stack.add(ck);
seen.add(ck);
}
}
}
return false;
}
}
Complexity Analysis
Time Complexity: O(N^2 \log N)O(N2logN). Our depth-first search during a call to
possible
is O(N^2)O(N2), and we make up to O(\log N)O(logN) of them.Space Complexity: O(N^2)O(N2), the maximum size of the stack.
方法2中的possible函数跟方法1很像,只不过没有用优先级队列,把排序的O(logN) 时间转移到二分搜索的O(logN) 时间了。