在一个 N × N N \times N N×N 的坐标方格 g r i d grid grid 中,每一个方格的值 g r i d [ i ] [ j ] grid[i][j] grid[i][j] 表示在位置 ( i , j ) (i,j) (i,j) 的平台高度。
现在开始下雨了。当时间为 t t t 时,此时雨水导致水池中任意位置的水位为 t 。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。
你从坐标方格的左上平台 ( 0 , 0 ) (0,0) (0,0) 出发。最少耗时多久你才能到达坐标方格的右下平台 ( N − 1 , N − 1 ) (N-1, N-1) (N−1,N−1)?
示例 1:
输入: [[0,2],[1,3]]
输出: 3
解释:
时间为
0
0
0时,你位于坐标方格的位置为
(
0
,
0
)
(0, 0)
(0,0)。
此时你不能游向任意方向,因为四个相邻方向平台的高度都大于当前时间为
0
0
0 时的水位。
等时间到达
3
3
3 时,你才可以游向平台
(
1
,
1
)
(1, 1)
(1,1). 因为此时的水位是
3
3
3,坐标方格中的平台没有比水位
3
3
3 更高的,所以你可以游向坐标方格中的任意位置
示例2:
输入: [[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]]
输出: 16
解释:
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
最终的路线用加粗进行了标记。
我们必须等到时间为
16
16
16,此时才能保证平台
(
0
,
0
)
(0, 0)
(0,0) 和
(
4
,
4
)
(4, 4)
(4,4) 是连通的
提示:
2 < = N < = 50. g r i d [ i ] [ j ] 是 [ 0 , . . . , N ∗ N − 1 ] 2 <= N <= 50.\\ grid[i][j] 是 [0, ..., N*N - 1] 2<=N<=50.grid[i][j]是[0,...,N∗N−1]的排列。
- 题意
注意题目中的重要信息:假定你可以 瞬间移动 无限距离,游动不耗时。当前这个问题就转换成为:找一个时刻 t,使得这个二维网格上数值 小于等于 t 的部分,存在一条从左上角到右下角的路径。即:经过了时间 t 以后,可以瞬间从左上角(坐标 [0, 0])游到右下角(坐标 [N - 1, N - 1])。 - 二分查找 + 遍历
class Solution {
public:
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
bool check(vector<vector<int>>g,int h){
int n=g.size();
queue<pair<int,int>>q;
int vis[51][51]={0};
if (g[0][0]<=h){
q.push({0,0});
vis[0][0]=1;
}else return 0;
while (!q.empty()){
int x=q.front().first,y=q.front().second;
q.pop();
for (int i=0;i<4;i++){
int nx=x+dx[i],ny=y+dy[i];
if (nx>=0&&nx<n&&ny>=0&&ny<n&&vis[nx][ny]==0&&g[nx][ny]<=h){
vis[nx][ny]=1;
if (nx==n-1&&ny==n-1) return 1;
q.push({nx,ny});
}
}
}
return 0;
}
int swimInWater(vector<vector<int>>& grid) {
int n=grid.size();
int l=0,r=n*n,ans=0;
while (l<=r){
int mid=l+r>>1;
if (check(grid,mid)){
r=mid-1;
ans=mid;
}else{
l=mid+1;
}
}
return ans;
}
};
- 并查集
由于题目要我们找的是最少等待时间,可以模拟下雨的过程,把网格抽象成一个无权图,每经过一个时刻,就考虑此时和雨水高度相等的单元格,考虑这个单元格的上、下、左、右、四个方向,并且高度更低的单元格,把它们在并查集中进行合并。
class Solution {
public:
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
int find(vector<int>&f,int x){
return x==f[x]?x:f[x]=find(f,f[x]);
}
void union1(vector<int>&f,int x,int y){
x=find(f,x);
y=find(f,y);
if (x!=y) f[x]=y;
}
int swimInWater(vector<vector<int>>& grid) {
int n=grid.size();
vector<int>a(n*n,0);
vector<int>f(n*n);
for (int i=0;i<n;i++){
for (int j=0;j<n;j++){
a[grid[i][j]]=i*n+j;
}
}
for (int i=0;i<n*n;i++) f[i]=i;
for (int i=0;i<n*n;i++){
int x=a[i]/n,y=a[i]%n;
for (int j=0;j<4;j++){
int nx=x+dx[j],ny=y+dy[j];
if (nx>=0&&ny>=0&&nx<n&&ny<n&&grid[nx][ny]<=i){
union1(f,a[i],nx*n+ny);
}
if (find(f,0)==find(f,n*n-1)) return i;
}
}
return 0;
}
};
- Dijkstra 算法
注意这个问题求的是从一个源点到目标顶点的最短路径,并且这条路径上的边没有负数(这一点非常重要,单元格的高度大于等于 0 0 0),符合 D i j k s t r a Dijkstra Dijkstra 算法的应用场景。为此我们可以把问题抽象成一个带权有向图,权值是有向边指向的顶点的高度。如下图所示:
class Solution {
private:
static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public:
int swimInWater(vector<vector<int>>& grid) {
int n = grid.size();
priority_queue<pair<int,int>,vector<pair<int,int>> ,greater<pair<int,int>> >q;;
q.push({grid[0][0],0});
vector<int> dist(n * n, INT_MAX);
dist[0] = grid[0][0];
vector<int> seen(n * n);
while (!q.empty()) {
pair<int,int> p=q.top();
int hi=p.first;
int id=p.second;
q.pop();
if (seen[id]) {
continue;
}
int x=id/n,y=id%n;
if (x == n - 1 && y == n - 1) {
break;
}
seen[id] = 1;
for (int i = 0; i < 4; ++i) {
int nx = x + dirs[i][0];
int ny = y + dirs[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && max(hi, grid[nx][ny]) < dist[nx * n + ny]) {
dist[nx * n + ny] = max(hi, grid[nx][ny]);
q.emplace(dist[nx * n + ny],nx * n + ny);
}
}
}
return dist[n*n - 1];
}
};