[POI2007] BZOJ 1102 山峰和山谷Grz-bfs-floodfill

16 篇文章 0 订阅

题目链接:右转进入题目

题目大意:请自行参考题目;

题解:一眼望过去就是个O(n^2)的爆搜。

但是有几个注意的地方:

1,题目的联通是八联通;

2,由于最多有10^6的点,所以dfs会爆栈;

3,每次找一个合法的联通块,如果搜索到一半发现不合法也要把这个联通块全部填充完,否则下一次再次搜索到这个联通块会很麻烦;

4,八个判断可以用dx和dy数组简化,这个常用技巧;

5,由于每次填充都要填充完,所以立一个flag=true判断是否合法。每次填充完了,if(flag) cnt++。

6,有些人可能会写的两个bfs(分别判断山峰和山谷),其实没有必要,只写一个山峰的;

      求山谷的时候,让原图中(x,y)的高度W(x,y)变为 -W(x,y)即可,然后对于这个“相反”图求一次山峰即可。(想一想为什么)

      其实这个就是如果不想手写堆又不懂C++的greater的话如何把STL里的priority_queue这个大根堆变成小根堆。

      做法就是把push(x)写成push(-x),top()写成-top(),即可(想一想为什么)。

7,如果遵循6的做法,辣么第二次bfs之前不要忘了把vis数组置为flase!

8,一个鬼畜错误,读入时候把:

scanf("%d",&h[i][j]);写成了scanf("%d",&h[i]);竟然没有警告!

害得我调了十分钟……

附上代码:

//POI2007
//BZOJ1102
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inrange(x,y) ((x)>=1&&(x)<=n&&(y)>=1&&(y)<=n)
#define MAXN 1010
using namespace std;
int h[MAXN][MAXN],n,cnt;
bool vis[MAXN][MAXN];
int dx[9]={0,-1,-1,-1,0,0,1,1,1},
	dy[9]={0,-1,0,1,-1,1,-1,0,1};
struct point{
	int x,y;
	point(int _x,int _y)
	{
		x=_x;y=_y;
	}
	point(const point &p)
	{
		x=p.x;y=p.y;
	}
};
queue<point> q;
int clearq(queue<point> &q)
{
	while(!q.empty()) q.pop();
	return 0;
}
int bfs(int x,int y)
{
	const int high=h[x][y];
	bool flag=true;
	vis[x][y]=true;
	clearq(q);q.push(point(x,y));
	while(!q.empty())
	{
		point p(q.front());q.pop();
		int px,py;
		for(int i=1;i<=8;i++)
		{
			px=p.x+dx[i],py=p.y+dy[i];
			if(inrange(px,py))
			{
				if(h[px][py]==high&&!vis[px][py])
				{
					vis[px][py]=true;
					q.push(point(px,py));
				}
				else if(h[px][py]>high)
					flag=false;
			}
		}
	}
	if(flag) cnt++;
	return (int)flag;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&h[i][j]),
			vis[i][j]=false;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(!vis[i][j]) bfs(i,j);
	printf("%d ",cnt);cnt=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			h[i][j]=-h[i][j],
			vis[i][j]=false;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(!vis[i][j]) bfs(i,j);
	printf("%d\n",cnt);return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值