CSP初赛复习-29-洪水填充-FloodFill
CSP初赛复习-29-洪水填充-FloodFill
洪水填充 flood fill
从一个起始节点开始,把附近与其连通的节点提取出或填充成不同颜色颜色,直到封闭区域内的所有节点都被处理过为止。
是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典算法。
因为其思路类似洪水从一个区域扩散到所有能到达的区域而得名
连通块
连通块(Connected Components),也称为连通分量,是图论中的一个概念。
一个无向图的连通块是指图中的一个最大子图,其中任意两个顶点之间存在一条路径。
具体来说,对于一个无向图 G = (V, E),其中 V 是顶点的集合,E 是边的集合,连通块可以定义为:对于任意两个顶点 u、v ∈ V,如果存在一条路径从顶点 u 到顶点 v,那么它们属于同一个连通块。
一个连通块是一个最大子图,即一个连通块中的任意顶点都不能与图外的顶点相连
洪水填充主要是找有多少个连通块及每个连通块的大小等
例题1 细胞数量 四邻域填充
题目描述
一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数
输入格式
第一行两个整数代表矩阵大小n和m (1<=m,n<=100)
接下来n行,每行一个长度为m的只含字符0到9的字符串,代表这个n * m的矩阵
输出格式
一行一个整数代表细胞个数
样例输入
4 10
0234500067
1034560500
2045600671
0000000089
样例输出
4
大致思路
1 遍历二维数组每一个点,如果是水并且没有被放问过,进行一次bfs
2 bfs 从四个方向把相邻区域的水域逐层都进行标识,形成一块水坑
3 进行遍历二维数组中其他点,找其他的水坑
4 每找到一个水坑,水坑对应变量ans+1
参考程序
#include<bits/stdc++.h>
using namespace std;
struct xy{//结构体二维矩阵中的坐标
int x,y;
};
queue<xy> q;//队列
int n,m,ans=0;//n行m列,ans为答案
int a[105][105];//存矩阵
bool used[105][105];//记录是否走过
int dx[4]={-1,1,0,0};//向上下左右走一步行号和列好的改变
int dy[4]={0,0,-1,1};
void bfs(int sx,int sy){//bfs
xy st;
st.x=sx;
st.y=sy;
used[sx][sy]=1;//起始点标识使用过
q.push(st);//起始点放入队列
while(!q.empty()){//如果队列不为空 上 下 左 右四个方向走
xy nw=q.front();//取出当前节点
q.pop();//队列弹出
for(int i=0;i<4;i++){//上 下 左 右
xy nxt=nw;//四个节点从当前节点开始
nxt.x+=dx[i];
nxt.y+=dy[i];
//a[nxt.x][nxt.y]==0 不是细胞不能走
//used[nxt.x][nxt.y]==1 已经走过不能走
if(a[nxt.x][nxt.y]==0 || used[nxt.x][nxt.y]==1) continue;
used[nxt.x][nxt.y]=1;//把连通的走过的点标识已走过
q.push(nxt);//刚走过的节点开始进行向 上 下 左 右 四个方向扩散
}
}
}
int main(){
cin>>n>>m;
memset(a,0,sizeof(a));//a数组设置为0
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);//输入n * m 矩阵
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){//遍历所有没标识过的点
if(used[i][j]==0 && a[i][j]!=0){//没走过 并且是细胞 bfs 连通
bfs(i,j);
ans++;//进入说明没有走过 是一个连通块ans++
}
}
}
cout<<ans;//输出有几个连通区域
return 0;
}
例题2 池塘计数 八邻域填充
题目描述
由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。我们用一个n * m((1<=m,n<=100)的网格图表示.每个网格中有水(W)或是旱地(.).一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。约翰想弄清楚他的田地已经形成了多少水坑。给出约翰田地的示意图,确定当中有多少水坑
输入格式
第1行,两个空格隔开的整数n和m
第2行到第n+1行,每行m个字符,每个字符是 W 或 . ,们表示网格图中的一排。字符之间没有空格
输出格式
输出一行,表示水坑的数
样例输入
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
大致思路
1 遍历二维数组每一个点,如果是水并且没有被放问过,进行一次bfs
2 bfs 从八个方向把相邻区域的水域逐层进行标识,形成一块水坑
3 进行遍历二维数组中其他点,找其他的水坑
4 每找到一个水坑,水坑对应变量ans+1
参考程序
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m,ans;//n行 m列 ans有几个连通的水坑
char a[N][N];//输入旱地和水坑
bool v[N][N];//是否被使用 被访问
//八个方向 左 左上 上 右上 右 右下 下 左下
int dx[]={-1,-1,0,1,1,1,0,-1};
int dy[]={0,-1,-1,-1,0,1,1,1};
/*
通过一个水点 把周围水点都标识被此水坑占用
*/
void bfs(int y,int x){
queue<pair<int,int> > q;//使用pair数据结构替代结构体 代表x y
v[y][x]=true;//标记当前点
q.push(make_pair(y,x));//当前点放入队列
while(!q.empty()){//队列不为空
int yy=q.front().first;
int xx=q.front().second;//取出当前数
q.pop();//出队
for(int i=0;i<8;i++){//扩展8个点
int yyy=yy+dy[i];//扩展后y
int xxx=xx+dx[i];//扩展后x
//不出界 是水 并且没被访问过
if(yyy>=0 && yyy<n && xxx>=0 && xxx<m
&& a[yyy][xxx]=='W' && !v[yyy][xxx]){
v[yyy][xxx]=true;//标记扩展点
q.push(make_pair(yyy,xxx));//放入扩展点到队列
}
}
}
}
int main(){
cin>>n>>m;//输入n行m列
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){//输入水和旱地到a数组
cin>>a[i][j];
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
//找到有水 并且没有被标识过的点 --说明找到一个水坑
//找到一个点后,会通过bfs把周边有水的点都标识被水坑占用
if(a[i][j]=='W' && !v[i][j]){
//一次bfs找到一个水坑(一个连通块) ans累加1
ans++;
bfs(i,j);
}
}
}
cout<<ans;//找到几个水坑 (几个连通块)
}
CSP初赛复习-29-洪水填充-FloodFill-练习题
作者:newcode 更多资源请关注纽扣编程微信公众号

从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习

浙公网安备 33010602011771号