题目描述
现有一块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 数组即可。h 数组初始化为 a 数组。
很多人第一时间想到的时深度优先搜索来枚举每一个格子,与周围的去比较,得出结果,再去搜索下一个格子,但是,枚举每一个格子的时候只考虑周围的格子,没有考虑更远的格子(如果更远的格子更高呢),样例举例:
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]}); //放进队列里面
}
到此结束,拜拜