翻译过来就是洪水覆盖,形象的表示这个算法将地图分为凹凸两种地形,以此来判断是否联通。
算法过程:如果要用水覆盖,则选择一个凹点,遍历它周围四个格子,如果也是凹格子则水可以漫延过去,再遍历所有新加入的点,判断是否可以漫延,上图中的黄色点最终都可以变为蓝色。
例题:池塘计数
农夫约翰有一片 N∗M 的矩形土地。
最近,由于降雨的原因,部分土地被水淹没了。
现在用一个字符矩阵来表示他的土地。
每个单元格内,如果包含雨水,则用”W”表示,如果不含雨水,则用”.”表示。
现在,约翰想知道他的土地中形成了多少片池塘。
每组相连的积水单元格集合可以看作是一片池塘。
每个单元格视为与其上、下、左、右、左上、右上、左下、右下八个邻近单元格相连。
请你输出共有多少片池塘,即矩阵中共有多少片相连的”W”块。
输入格式
第一行包含两个整数 N 和 M。
接下来 N 行,每行包含 M 个字符,字符为”W”或”.”,用以表示矩形土地的积水状况,字符之间没有空格。
输出格式
输出一个整数,表示池塘数目。
数据范围
1 ≤ N,M ≤ 1000
输入样例:
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
输出样例:
3
注意此题与模板有一处不同:本题求的是八联通
#include<cstring>
#include<algorithm>
#include<iostream>
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int N=1010,M=N*N;
int n,m;//长宽
char g[N][N];
pii q[M];
bool st[N][N];
void bfs(int sx,int sy)//传入起点和终点
{
int hh=0,tt=0;//模拟队列的队首和队尾
q[0]={sx,sy};
st[sx][sy]=true;//标记变量
while(hh<tt)
{
pii t=q[hh++];
for(int i=t.x-1;i<=t.x+1;i++)
for(int j=t.y-1;j<=t.y+1;j++)
{
if(i==t.x&&j==t.y)continue;//去掉中间
if(i<0||i>=n||j<0||j>=m)continue;//超出范围
if(g[i][j]=='.'||st[i][j]) continue;
q[++tt]={i,j};
st[i][j]=true;
}
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",g[i]);
}
int cnt=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(g[i][j]=='W'&&!st[i][j])
bfs(i,j);
cnt++;
}
}
printf("%d",cnt);
}
细节分析:
1.在遍历方格时,如果遇到池塘,会将与当前池塘联通的格子都标记,记为一个连通块,再往后搜索时,只会将没遍历过即不属于任何一个连通块的计算入
2.枚举周围格子时,有四联通和八联通等等,八联通通常就是如上,把中间格子扣掉
四联通通常是设置偏移量,如:
dx[ 4 ] = {0,-1,0,1},dy[ 4 ] = {-1,0,1,0};
for(int i = 0; i < 4;i ++)
{
int a = t.x + dx[i],b = t.y + dy[i];
if(a < 0||a >= n||b < 0||b >= m)//边界判断
continue;
if(st[a][b])
continue;
if(g[t.x][t.y]>>i&1) continue;
q[ ++tt] = {a,b};
st[a][b] = true;
}