BZOJ 2936 [Poi1999]降 水 (FloodFill)优先队列+BFS

Description

有这样一块土地,它可以被划分成N×M个正方形小块,每块面积是一平方英寸,第i行第j列的小块可以表示成P(i,j)。这块土地高低不平,每一小块地P(i,j)都有自己的高度H(i,j)(单位是英寸)。
一场倾盆大雨后,这块地由于地势高低不同,许多低洼地方都积存了不少降水。假如你已经知道这块土地的详细信息,你能求出它最多能积存多少立方英寸的降水么?
 

Input

输入文件第一行有两个数,N,M(1<=N, M <=100),表示土地的规模是N×M平方英寸。
以下有N行,每行有M个整数,表示每块地的高低(每个整数在[1,10000]内,以英寸为单位)。
 

Output

输出文件只有一行,一个数,表示土地中最多能积存多少立方英寸的水。
 

Sample Input

3 6
3 3 4 4 4 2
3 1 3 2 1 4
7 3 1 6 4 1

Sample Output

5

HINT











考试竟然考了这道的原题= =优化加错了结果竟然考试里还A了= =

一个很简单的想法是,每次找出最小高度的点,
然后向四周扩展到极点,并且确定出它能上升的最大高度,并且填满;
如何扩展?只要用个BFS,每次向四周值相等的位置扩展即可;
对于某一个位置,确定它上升的幅度可以重新BFS一遍。。
那么知道了每个位置最后变成了什么样,就可以知道变化量了,累加即可。。
注意边界都是0。。
感觉有点说不清楚,不过可以看代码之类的吧。。=v=

那么在最坏O((NM)^2*log(NM))的时间内可以解决这道题,,
显然数据如果精心构造,也就是N*M个数全部都相等,那么每次都要BFS完整的一遍,
于是花式TLE了……
怎么解决呢……我们可以用一个vis[x][y]来加速。
假设一遍BFS之后,我们发现水没有任何上涨,
那就再重新跑一遍,并且走过的所有的点(x,y),vis[x][y]=1。

这样子的话我们看到,确实这种数据可以AC了,但是时间复杂度保证了吗?
其实是可以证明的,在最坏的情况下,时间复杂度也是可观的;
常数当然大,,因为无故多跑了一遍bfs……

事实上这题正规点的做法都是SPFA之类的……
可以百度。
毕竟我这种做法还是很玄学的……





#include<bits/stdc++.h>
using namespace std;
const int 
	N=105,
	inf=20000;
int n,m,ans,tag;
int b[N][N],a[N][N],flag[N][N];
bool vis[N][N];

struct node{int high,x,y;};
priority_queue<node>Heap;
bool operator <(node a,node b){return a.high>b.high;}
bool operator >(node a,node b){return a.high<b.high;}

struct QUEUE{int x,y;}Q[N*N];
void BFS(int xstart,int ystart,int num,int f){
	int head=0,tail=1;
	Q[0]=(QUEUE){xstart,ystart};
	flag[xstart][ystart]=tag;
	while (head<tail){
		int x=Q[head].x,y=Q[head++].y;
		if (f) vis[x][y]=1;
		if (a[x-1][y]!=num) ans=min(ans,a[x-1][y]);
		if (a[x+1][y]!=num) ans=min(ans,a[x+1][y]);
		if (a[x][y-1]!=num) ans=min(ans,a[x][y-1]);
		if (a[x][y+1]!=num) ans=min(ans,a[x][y+1]);
		if (x==1 && num>=0) ans=0;
		if (y==1 && num>=0) ans=0;
		if (x==n && num>=0) ans=0;
		if (y==m && num>=0) ans=0;

		if (x>1 && flag[x-1][y]!=tag && a[x-1][y]==num) flag[x-1][y]=tag,Q[tail++]=(QUEUE){x-1,y};
		if (x<n && flag[x+1][y]!=tag && a[x+1][y]==num) flag[x+1][y]=tag,Q[tail++]=(QUEUE){x+1,y};
		if (y>1 && flag[x][y-1]!=tag && a[x][y-1]==num) flag[x][y-1]=tag,Q[tail++]=(QUEUE){x,y-1};
		if (y<m && flag[x][y+1]!=tag && a[x][y+1]==num) flag[x][y+1]=tag,Q[tail++]=(QUEUE){x,y+1};
	}
}
void change(int xstart,int ystart,int num,int now){
	int head=0,tail=1;
	Q[0]=(QUEUE){xstart,ystart};
	flag[xstart][ystart]=tag;
	while (head<tail){
		int x=Q[head].x,y=Q[head++].y;
		a[x][y]=now;
		if (x>1 && flag[x-1][y]!=tag && a[x-1][y]==num) flag[x-1][y]=tag,Q[tail++]=(QUEUE){x-1,y};
		if (x<n && flag[x+1][y]!=tag && a[x+1][y]==num) flag[x+1][y]=tag,Q[tail++]=(QUEUE){x+1,y};
		if (y>1 && flag[x][y-1]!=tag && a[x][y-1]==num) flag[x][y-1]=tag,Q[tail++]=(QUEUE){x,y-1};
		if (y<m && flag[x][y+1]!=tag && a[x][y+1]==num) flag[x][y+1]=tag,Q[tail++]=(QUEUE){x,y+1};
	}
}
void solve(){
	while (!Heap.empty()){
		node u=Heap.top();Heap.pop();
		if (vis[u.x][u.y] || a[u.x][u.y]-b[u.x][u.y]) continue;
		ans=inf,tag++;
		BFS(u.x,u.y,u.high,0);
		if (!ans) tag++,BFS(u.x,u.y,u.high,1);
		if (ans<=u.high) continue;
		tag++,change(u.x,u.y,u.high,ans);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			scanf("%d",&a[i][j]),b[i][j]=a[i][j],
			Heap.push((node){a[i][j],i,j});
	solve();
	int answer=0;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++) answer+=max(a[i][j]-b[i][j],0);
	printf("%d\n",answer);
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值