LeecCode:407 接雨水 II 优先队列+BFS

题目

给定一个 m x n 的矩阵,其中的值均为正整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。

说明:

m 和 n 都是小于110的整数。每一个单位的高度都大于 0 且小于 20000。

示例:

给出如下 3x6 的高度图:

[
  [1,4,3,1,3,2],
  [3,2,1,3,2,4],
  [2,3,3,2,3,1]
]

返回 4。

如上图所示,这是下雨前的高度图
[[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] 的状态。

在这里插入图片描述

下雨后,雨水将会被存储在这些方块中。总的接雨水量是4。
在这里插入图片描述

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

这题一开始我是想仿造【LeetCode 42:接雨水】的思路,即当前连通分量中的边界最小值,都大于当前点,那么这个点就可以装水,bfs计算连通分量内的最小高度,但是超时

扫描二维码关注公众号,回复: 10088895 查看本文章

超时代码(正确的在下边

class Solution {
public:
    typedef struct p
    {
        int x, y, h;
        p():x(0), y(0), h(0){}
        p(int a, int b, int c):x(a),y(b),h(c){}
    }p;
    int trapRainWater(vector<vector<int>>& heightMap)
    {
        if(heightMap.size()<3) return 0;
        if(heightMap[0].size()<3) return 0;
        int m=heightMap.size(), n=heightMap[0].size();
        int biasX[4]={-1,0,1,0}, biasY[4]={0,-1,0,1};
        int ans = 0;
        for(int i=1; i<m-1; i++)
        {
            for(int j=1; j<n-1; j++)
            {
                int minh=INT_MAX;
                queue<p> q; q.push(p(i, j, heightMap[i][j]));
                vector<p> path;
                bool vis[m][n]; memset(vis, false, sizeof(vis)); vis[i][j]=true;
                while(!q.empty())
                {
                    p tp=q.front(); q.pop(); path.push_back(tp); 
                    for(int b=0; b<4; b++)
                    {
                        int nx=tp.x+biasX[b], ny=tp.y+biasY[b];
                        if(0<=nx && nx<m && 0<=ny && ny<n)
                        {
                            if(!vis[nx][ny])
                            {
                                if(heightMap[nx][ny]>heightMap[tp.x][tp.y])
                                    minh = min(minh, heightMap[nx][ny]);
                                else
                                    q.push(p(nx, ny, heightMap[nx][ny])), vis[nx][ny]=true;  
                            }
                        }
                        else minh = 0;
                    }
                }
               // cout<<i<<" "<<j<<" "<<minh<<" "<<maxh<<"-"<<endl;
                if(heightMap[i][j]>=minh) continue;
                for(int k=0; k<path.size(); k++)
                {
                    ans += minh - heightMap[path[k].x][path[k].y];
                    heightMap[path[k].x][path[k].y] = minh;
                }
                i=1; j=1;
                //cout<<i<<" "<<j<<" "<<ans<<endl;
            }
        }
        return ans;
    }
};

正确思路

因为能够装多少水,是由当前已知边界的最低点决定的
在这里插入图片描述
维护一个优先队列作为当前已知边界的集合,方便快速获取已知边界的最低点,每次选择最低的边界,查看边界四周的格子能否装水

四周的格子一共就两种情况:能装水,不能装水

如果周边的格子能装水,即矮于当前最低边界

如果已知的最低边界,都要高于它旁边的格子,那么这个格子装水之和必定会变成这样,也就是水高不过已知的最低边界
在这里插入图片描述
因为这个格子装了水,那么以后不再考虑它,也就是,我们把边界更新了,注水的格子和墙是无异的,我们认为新边界是和旧边界一样高的

在这里插入图片描述

如果周边的格子不能装水,即高于当前最低边界

如果当前格子比当前已知最低边界还要高,那么边界将被更新,也就是将当前格子加入边界集合,而旧边界则退出边界集合

在这里插入图片描述

总体思路是:

  • 维护一个优先队列作为当前已知边界的集合,初始元素是矩阵四边的元素
  • 每次选取最低的已知边界x,将它从集合中删除
  • 检查最低边界x周围的格子g,格子g的高度是gh,如果低于x,那么可以装水,计算水量,然后将【坐标为g,高度为x】的边界加入集合,因为这里是用最低的边界做计算,保证了正确性,而加入则是加等价边界
  • 如果高于,那么可以更新边界,将【坐标为g,高度为gh】的边界加入集合
  • 注意对操作过的边界添加visited访问控制,以免重复访问

代码

class Solution {
public:
    typedef struct p
    {
        int x, y, h;
        p():x(0), y(0), h(0){}
        p(int a, int b, int c):x(a),y(b),h(c){}
        bool operator < (const p &p1) const {return p1.h<h;}
    }p;
    int trapRainWater(vector<vector<int>>& heightMap)
    {
        if(heightMap.size()<3) return 0;
        if(heightMap[0].size()<3) return 0;
        int m=heightMap.size(), n=heightMap[0].size();
        int biasX[4]={-1,0,1,0}, biasY[4]={0,-1,0,1}, ans = 0;
        bool vis[m][n]; memset(vis, false, sizeof(vis));
        priority_queue<p> q;
        // 初始化:将矩形的四边加入边界集合
        for(int i=0; i<m; i++)
        {
            q.push(p(i,n-1,heightMap[i][n-1])); vis[i][n-1]=true;
            q.push(p(i,0,heightMap[i][0])); vis[i][0]=true;
        }
        for(int i=1; i<n-1; i++)
        {
            q.push(p(0, i, heightMap[0][i])); vis[0][i]=true;
            q.push(p(m-1, i, heightMap[m-1][i])); vis[m-1][i]=true;
        }    
        while(!q.empty())
        {
            // 每次选取最低的已知边界,更新四周
            p tp=q.top(); q.pop();
            for(int b=0; b<4; b++)
            {
                int nx=tp.x+biasX[b], ny=tp.y+biasY[b];
                if(0<=nx && nx<m && 0<=ny && ny<n && !vis[nx][ny])
                {
                    vis[nx][ny] = true;
                    q.push(p(nx, ny, max(heightMap[nx][ny], tp.h)));
                    ans += max(0, tp.h-heightMap[nx][ny]);
                }
            }
        }
        return ans;
    }
};
发布了238 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/104870846