用水填坑(优先队列、宽搜)

原题链接

题目描述

现有一块n*m的地,每块1*1的地的高度为hi,j,在n*m的土地之外的土地高度均为0(即你可以认为最外圈无法积水)

现在下了一场足够大的雨,试求出这块地总共积了多少单位体积的水

输入描述:

第一行两个数 n, m,具体含义见题意

接下来 n 行每行 m 个数, 第 i 行为 hi,1, hi,2, ..., hi,m

输出描述:

输出一行一个数表示积水的总量

样例1:

输入:

5 5
0 0 5 0 0
0 4 3 8 2
9 5 7 2 7
1 9 6 5 4
1 0 0 6 2

输出:

4

样例2:

输入:

10 10
0 0 0 0 0 0 0 0 0 0
0 5 2 6 4 3 1 7 8 0
0 6 4 2 3 5 1 4 6 0
0 3 6 4 1 2 4 7 8 0
0 2 5 5 1 2 3 4 4 0
0 2 3 1 5 4 4 1 4 0 
0 4 1 2 3 4 5 2 1 0
0 7 5 5 1 5 4 5 7 0
0 1 3 5 5 4 6 8 7 0
0 0 0 0 0 0 0 0 0 0

输出:

23

题解:

这题很明显是一道搜索题,既然要求积水体积,那么要计算的是那些四周比这个格子高的地方。根据木桶原理得,如果一个格子周围都比它高,那么这个格子能容纳它周围最小的高度差

所以

我们存放答案时需要两个数组,一个是原数组a[][],另一个是 [i,j]格 积水后的高度h[][],最后循环累加高度差就可以了。如果要这样做,我们在搜索的过程中处理 h 数组即可。数组初始化为 数组。

很多人第一时间想到的时深度优先搜索来枚举每一个格子,与周围的去比较,得出结果,再去搜索下一个格子,但是,枚举每一个格子的时候只考虑周围的格子,没有考虑更远的格子(如果更远的格子更高呢),样例举例:

  8 9 
7 3 5 7
  8 8

假如搜索到3这个格子了,那么h数组存放时会存放成周围最低的格子5,可是当搜索到5这个格子的时候,因为左边的格子和它一样高了,所以h数组存放是还是5。而正确答案是两个格子的h数组都是7,所以深度优先搜索是不行的(除非你是犇犇)。

故考虑广度优先搜索,由于土地之外的高度均为0,所以土地的边缘不能积水的,我们从这里搜起,由于木桶原理,所以我们要选择最低的格子,在搜索中我们要用这个格子去改变下一个格子。如果下一个格子的高度低于这个格子的高度,那么就 h[下一个格子]=h[这个格子]。(搜索部分)

这时候有人问了,怎么去选择最低的格子呢?每搜一个都去打一遍擂台吗?这明显不行,这时候就要用到神奇的优先队列priority_queue了,因为它系统默认大根堆,而大根堆是按从大到小排的(非常的难以置信),因此我们要用到重载运算符,并且按高度从小到大排(毕竟还要存位置)

优先队列的定义(顺便坐标数组):

int dx[4]={-1,0,0,1};
int dy[4]={0,-1,1,0};
struct Node{
	int x,y,h;
	bool operator < (const Node &a)const{return h>a.h;};	
};
priority_queue<Node> q;

优先队列的使用(这道题):

q.push(Node{i,j,a[i][j]});  //放进队列里面
Node x=q.top();  //获得堆顶元素,存到相应的结构体内
q.pop();  //弹出堆顶元素

搜索部分:

while(!q.empty()){
	Node x=q.top();  //取出堆顶元素
	q.pop();
	int th=x.h;
	for(int i=0;i<4;i++){
		int xx=dx[i]+x.x;
		int yy=dy[i]+x.y;
		if(xx<1 || xx>n || yy<1 || yy>m || v[xx][yy]) continue;  //超出边界或已经遍历过了
		h[xx][yy]=max(h[xx][yy],th);  //取积水后的高度最大
		v[xx][yy]=1;  //标记
		q.push(Node{xx,yy,h[xx][yy]});  //放进队列中,以后搜
	}
}

因为我们要从边缘搜起,所以先要将边缘的格子数据放进队列里面,所以在输入的部分:

if(i==1 || i==n || j==1 || j==m){  //如果是边缘
	v[i][j]=1;  //标记为1
	q.push(Node{i,j,a[i][j]});  //放进队列里面
}

到此结束,拜拜

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值