水位上升的泳池中游泳

在一个 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) (00) 出发。最少耗时多久你才能到达坐标方格的右下平台 ( N − 1 , N − 1 ) (N-1, N-1) (N1,N1)

示例 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,...,NN1]的排列。

  1. 题意
    注意题目中的重要信息:假定你可以 瞬间移动 无限距离,游动不耗时。当前这个问题就转换成为:找一个时刻 t,使得这个二维网格上数值 小于等于 t 的部分,存在一条从左上角到右下角的路径。即:经过了时间 t 以后,可以瞬间从左上角(坐标 [0, 0])游到右下角(坐标 [N - 1, N - 1])。
  2. 二分查找 + 遍历
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;

    }
};
  1. 并查集
    由于题目要我们找的是最少等待时间,可以模拟下雨的过程,把网格抽象成一个无权图,每经过一个时刻,就考虑此时和雨水高度相等的单元格,考虑这个单元格的上、下、左、右、四个方向,并且高度更低的单元格,把它们在并查集中进行合并。
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;
    }
};
  1. 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];
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值