每天6道题之第二十题:接雨水2(堆)

前言

在这里插入图片描述

题目描述

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

呜呜呜 有一维的接雨水还不够,还来了一个二维的…

解题思路

看这个吧

  • 把每一个元素称作块。因为那个图片给的好像瓷砖啊。
  • 其实做这题一开始都是想的是对于每一个块,去找它四个方向最高的高度中的最小值(二维下则是左右最高的高度取较小的那一个)作为上界,当前块作为下界
    但是这4个方向每次遍历复杂度过高,且不能像二维那样去提前预存每个方向的最大值
  • 那可以反过来我不以每个块为处理单元,而是以块的四周作为处理单元
  • 那如何保证所有四周的可能性都考虑到呢? 我们从矩阵的最外围往里面遍历,像一个圈不断缩小的过程
  • 为了防止重复遍历用visited记录
  • 其次要用小顶堆(以高度为判断基准)来存入所有快的四周(即圈是不断缩小的,小顶堆存的就是这个圈)
  • 为什么要用小顶堆? 这样可以保证高度较小的块先出队
    ** 为什么要让高度较小的块先出队?(关键点)
    1. 一开始时候就讲了基础做法是:对于每一个块,去找它四个方向最高的高度中的最小值(二维下则是左右最高的高度取较小的那一个)作为上界,当前块作为下界
    2. 而我们现在反过来不是以中心块为处理单元,而是以四周作为处理单元
    3. 我们如果能确保当前出队的元素对于该中心块来说是它周围四个高度中的最小值那么就能确定接雨水的大小
    4. 为什么队头元素的高度比中心块要高它就一定是中心块周围四个高度中的最小值呢?
      因为我们的前提就是小顶堆:高度小的块先出队,所以对于中心块来说,先出队的必然是中心块四周高度最小的那一个
  • 步骤:
    1. 构建小顶堆,初始化为矩阵的最外围(边界所有元素)
    2. 不断出队,倘若队头元素大于其四周的
      即代表能够接雨水:队头元素减去该中心块即当前中心块能接雨水的值
    3. 但是接完雨水之后中心块还要存进队列中,但这时要存入的中心块是接完雨水后的中心块

超详细接雨水2动画解释

代码


#include<iostream>
#include<queue> 
#include<vector>
using namespace std;

class Solution{
	public:
		int trapRainWater(vector<vector<int>> &heights){
			int n=heights.size();     //n是行 
			int m=heights[0].size();   //m是列 
			//priority_queue<pair<int,int>,vector<pair<int,int>>,decltype(&cmp) > q(cmp);
			priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> q;
			for(int i=0;i<n;i++){
				for(int j=0;j<m;j++){
					if(i == 0 || j==0 ||i==n-1 || j==m-1){
						//将周围一圈入优先级队列
						q.push({heights[i][j],i*m+j}) ;
						heights[i][j]=-1;
					}
				}
			}
			
			int x[4]={0,0,1,-1},y[4]= {1,-1,0,0};
			int ans=0;
			int mh=0; 
			while(!q.empty()){
				auto &cur=q.top();
				int h=cur.first;
				int i=cur.second/m;//i,j表示的是当前的坐标 
				int j=cur.second%m;
				q.pop();
				if(h > mh)  mh=h;
				for(int k=0;k<4;k++){
					i=i+x[k];
					j=j+y[k];
					if(i>=0 && i<n && j>=0 &&j<m && heights[i][j]!=-1){
						q.push({heights[i][j],i*m+j});
						if(heights[i][j]<mh){
							ans=ans+mh-heights[i][j];
							heights[i][j]=-1;
						}						
					}
					i=i-x[k];//改变方向 
					j=j-y[k];
				}
				} 
			return ans; 		
			} 
};

int main(){
	int m,n;//m是行 n是列
	int data;
	vector<vector<int>> heights;
	cin>>m>>n;
	for(int i=0;i<m;i++){
		vector<int> row;
		for(int j=0;j<n;j++){
			cin>>data;
			row.push_back(data);
		}
		heights.push_back(row);
	} 
	
	int res=Solution().trapRainWater(heights);
	cout<<res<<endl;
	return 0; 
	
	
	
	
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值