题目链接:右转进入题目
题目大意:请自行参考题目;
题解:一眼望过去就是个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;
}