Leetcode 778. Swim in Rising Water
Problem:
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].
Solution:
这两天刷了几道非常规的Binary Search题,总算是有了点感觉。看到这道题也是第一时间内想到了解法,还是挺爽的。这道题由于游泳速度是无限大的,所以我们只需要找到一个最小值,使得在这个矩阵内存在路径从左上角走到右下角,题目中还有个隐藏条件,就是矩阵的元素范围是0-N*N,所以直觉告诉我可以用Binary Search,而且也是那种非常规的Binary Search。通过二分法找到left和right的中间值pivot,然后去验证在pivot时间时是否存在从左上角到右下角的路径,当然也可以用BFS或DFS来验证,但我比较喜欢Union Find这种比较优雅的方式。这里的Union Find我取消了rank函数,并把二位数组压缩为一维,因为这里Union的原则是往索引值小的那一方Union,最后确认N*N-1的根节点是否为0即可。
Code:
1 class Solution { 2 public: 3 int Find(vector<int> &parent,int target){ 4 if(parent[target] == target) 5 return target; 6 return Find(parent,parent[target]); 7 } 8 void Union(vector<int> &parent,int x,int y){ 9 int px = Find(parent,x); 10 int py = Find(parent,y); 11 if(px == py) return; 12 if(px > py) parent[px] = py; 13 else parent[py] = px; 14 } 15 int swimInWater(vector<vector<int>>& grid) { 16 int m = grid.size(); 17 int left = 0; 18 int right = m*m-1; 19 while(left < right){ 20 int pivot = left+(right-left)/2; 21 vector<int> parent(m*m); 22 for(int i = 0;i != m*m;++i) 23 parent[i] = i; 24 for(int i = 0;i != m;++i){ 25 for(int j = 0;j != m;++j){ 26 if(grid[i][j] > pivot) continue; 27 if(i-1 >= 0 && grid[i-1][j] <= pivot) Union(parent,i*m+j,(i-1)*m+j); 28 if(i+1 < m && grid[i+1][j] <= pivot) Union(parent,i*m+j,(i+1)*m+j); 29 if(j-1 >= 0 && grid[i][j-1] <= pivot) Union(parent,i*m+j,i*m+j-1); 30 if(j+1 < m && grid[i][j+1] <= pivot) Union(parent,i*m+j,i*m+j+1); 31 } 32 } 33 if(Find(parent,m*m-1) == 0) 34 right = pivot; 35 else 36 left = pivot+1; 37 } 38 return left; 39 } 40 };