leetcode 407 Trapping Rain Water II

Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining.

 

Note:

Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000.

 

Example:

Given the following 3x6 height map:
[
  [1,4,3,1,3,2],
  [3,2,1,3,2,4],
  [2,3,3,2,3,1]
]

Return 4.

The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] before the rain.

 

After the rain, water is trapped between the blocks. The total volume of water trapped is 4.

 

给定一个 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。

 

解题思路:

首先我们应该能分析出,能装水的底面肯定不能在边界上,因为边界上的点无法封闭,那么所有边界上的点都可以加入 queue,当作 BFS 的启动点,同时我们需要一个二维数组来标记访问过的点,访问过的点我们用红色来表示,那么如下图所示:

 

我们再想想,怎么样可以成功的装进去水呢,是不是周围的高度都应该比当前的高度高,形成一个凹槽才能装水,而且装水量取决于周围最小的那个高度,有点像木桶原理的感觉,那么为了模拟这种方法,我们采用模拟海平面上升的方法来做,我们维护一个海平面高度 mx,初始化为最小值,从1开始往上升,那么我们 BFS 遍历的时候就需要从高度最小的格子开始遍历,那么我们的 queue 就不能使用普通队列了,而是使用优先级队列,将高度小的放在队首,最先取出,这样我们就可以遍历高度为1的三个格子,用绿色标记出来了,如下图所示:

 

 

如上图所示,向周围 BFS 搜索的条件是不能越界,且周围格子未被访问,那么可以看出上面的第一个和最后一个绿格子无法进一步搜索,只有第一行中间那个绿格子可以搜索,其周围有一个灰格子未被访问过,将其加入优先队列 queue 中,然后标记为红色,如下图所示:

 

 

那么优先队列 queue 中高度为1的格子遍历完了,此时海平面上升1,变为2,此时我们遍历优先队列 queue 中高度为2的格子,有3个,如下图绿色标记所示:

 

 

我们发现这三个绿格子周围的格子均已被访问过了,所以不做任何操作,海平面继续上升,变为3,遍历所有高度为3的格子,如下图绿色标记所示:

 

 

由于我们没有特别声明高度相同的格子在优先队列 queue 中的顺序,所以应该是随机的,其实谁先遍历到都一样,对结果没啥影响,我们就假设第一行的两个绿格子先遍历到,那么那么周围各有一个灰格子可以遍历,这两个灰格子比海平面低了,可以存水了,把存水量算出来加入结果 res 中,如下图所示:

 

 

上图中这两个遍历到的蓝格子会被加入优先队列 queue 中,由于它们的高度小,所以下一次从优先队列 queue 中取格子时,它们会被优先遍历到,那么左边的那个蓝格子进行BFS搜索,就会遍历到其左边的那个灰格子,由于其高度小于海平面,也可以存水,将存水量算出来加入结果 res 中,如下图所示:

 

 

等两个绿格子遍历结束了,它们会被标记为红色,蓝格子遍历会先被标记红色,然后加入优先队列 queue 中,由于其周围格子全变成红色了,所有不会有任何操作,如下图所示:

 

 

此时所有的格子都标记为红色了,海平面继续上升,继续遍历完优先队列 queue 中的格子,不过已经不会对结果有任何影响了,因为所有的格子都已经访问过了,此时等循环结束后返回res即可。

 

通过模拟海平面上升这个方法真的太巧妙了(模拟海水倒灌),可以应用于类似的收集雨水等模型的题目中。

为什么选择边界点作为启动点呢?因为边界格子不能收集雨水,而队列中的元素都是已经访问过的元素,不会考虑其收集的雨水量。

这里还有一个小技巧就是当遍历当前节点周围的节点是,用了dir这个二维向量,只要遍历一遍这个二维向量就相当于遍历了当前节点周围的所有节点,省去了大量的ifelse语句。

vector<vector<int>> dir = {{-1 , 0} , {0 , 1} , {1 , 0} , {0 , -1}} ;
for(auto d : dir)
            {
                int y = row + d[0] ;
                int x = col + d[1] ;
                
                if(y < 0 || y >= row_len || x < 0 || x >= col_len || visited[y][x]) continue ;
                
                if(sea_level > heightMap[y][x] ) ans += sea_level - heightMap[y][x] ;
                visited[y][x] = true ;
                q.push({heightMap[y][x] , y * col_len + x}) ;
            }
class Solution {
public:
    int trapRainWater(vector<vector<int>>& heightMap) 
    {
        if(heightMap.empty()) return 0 ;
        
        int row_len = heightMap.size() ;
        int col_len = heightMap[0].size() ;
        int sea_level = 0 ;
        int ans = 0 ;
        
        priority_queue<pair<int , int> , vector<pair<int , int>> , greater<pair<int , int>>> q ;
        vector<vector<bool>> visited(row_len , vector<bool>(col_len , false)) ;
        vector<vector<int>> dir = {{-1 , 0} , {0 , 1} , {1 , 0} , {0 , -1}} ;
        
        for(int i = 0 ; i < row_len ; i++)
        {
            for(int j = 0 ; j < col_len ; j++)
            {
                if(i == 0 || i == row_len - 1 || j == 0 || j == col_len - 1) 
                {
                    q.push({heightMap[i][j] , i * col_len + j}) ;
                    visited[i][j] = true ;
                }
            }
        }
        
        while(!q.empty())
        {
            pair<int, int> p = q.top() ;
            int row = p.second / col_len ; 
            int col = p.second % col_len ;
            q.pop() ;
            sea_level = (sea_level >= p.first ? sea_level : p.first) ; // sea_level = max(sea_level , p.first) ;
            
            for(auto d : dir)
            {
                int y = row + d[0] ;
                int x = col + d[1] ;
                
                if(y < 0 || y >= row_len || x < 0 || x >= col_len || visited[y][x]) continue ;
                
                if(sea_level > heightMap[y][x] ) ans += sea_level - heightMap[y][x] ;
                visited[y][x] = true ;
                q.push({heightMap[y][x] , y * col_len + x}) ;
            }
                        
        }
        
        return ans ;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值