BFS(广度优先搜索算法)之细胞例题
目录
一 问题描述
例子如下:输入4行10列的矩阵,根据题意,可以数出在该例子中细胞数目共有4个
![]()
如下:输入3行10列的矩阵,根据题意,可以数出在该例子中细胞数目共有1个
二 解题思路
该题可以使用广度优先搜索算法(BFS)来做,我们以图1为例讲一讲解题思路
通过观察,可以发现,被0单独围起来的数字区域总共有4个,因此该图的细胞数为4,利用BFS将每一个数字区域中1-9之间的数进行扫描,然后将数值全部赋值为某个非1-9之间的数,比如说-1。每当扫描完一个区域,我们便将细胞的计数器+1,单个区域的扫描可以通过BFS函数来实现,在这里,我将这个函数取名为scan,在主函数中,找出所有的1-9数值之间的元素,每判断到当前元素的值在1-9之间,便调用scan函数进行单个区域的扫描和赋值,也就是意味着,scan被调用了几次,那么最终图中的细胞数就会有几个。
在主函数中找出数值在1-9之间的元素,从此元素出发,调用scan函数,寻找数值区域,将数值区域重新赋值,并且细胞计数器+1
第一次:从a[0][0]开始判断,发现a[0][0]中的数值不在1-9之间,说明从当前点出发并无数字区域可以扫描,因此找寻下一个元素a[0][1],找到a[0][1]的时候发现,它的数值落在1-9范围之内,说明从当前点a[0][1]出发有数字区域可以扫描,于是调用scan函数,将此元素的下标位置(0,1)作为参数传递给scan函数,于是scan函数就从(0,1)开始进行数字区域的扫描,scan函数就是利用了BFS的思想,将从(0,1)出发的数字区域进行扫表,并且扫描过后该区域所有的元素都被赋值为-1(也可以是别的,只要不在1-9范围内即可),代表该区域已经被访问过,细胞计数器+1,此时,数据变为:
第二次:刚刚上一步a[0][1]已经访问过了,并且a[0][1]出发的区域也已经被全部扫描过了,可以通过上图看出来,接下去在主函数中应该是要判断a[0][2]这个元素了,我们可以发现,此时a[0][2]这个元素中的数值已经被上一次的区域扫描而被赋值成1了,因此并不会调用scan函数,在主函数中继续遍历a[0][3],a[0][4],发现他们两个都是-1,所以scan函数也不会被调用,继续扫描a[0][5],发现a[0][5]的值是0,所以从这个点出发也不会有数据区域,依此类推,当遍历到a[0][8]的时候,发现它的数值落在1-9范围之间,那就可以从此元素出发,调用scan函数,将(0,8)这个下标作为参数传递给scan函数,在scan函数中进行BFS扫描,并且将从这个点出发扫描后的数字区域全部赋值为-1,代表该区域已经被访问过了,细胞计数器+1,此时,数据变为:
第三次:刚刚上一步a[0][8]已经访问过了,接下来继续访问a[0][9],发现它的值为-1,受了上一个数据区域扫描的影响,在主函数中,我们判断过后,它的值不在1-9这个范围之内,我们可以不再访问,依次类推,当在主函数中访问到a[1][0],发现它的值落在1-9范围之间,那么从这个元素出发,进行BFS的扫描,将下标(1,0)传递给scan函数,此时scan函数被调用,然后在scan函数中从(1,0)出发,扫描1所在的那个数据区域,并将扫描过的元素都被赋值为-1,代表该区域也已经被访问过了,细胞计数器+1,此时,数据变成:
第四次:刚刚上一步a[1][0]已经被访问过了,继续访问a[1][1],a[1][2],a[1][3]......a[1][6],发现它们都不满足调用scan函数的条件,因为范围都不在1-9之内,当访问到a[1][7]的时候,发现它的数值5落在1-9范围之内,于是从下标(1,7)出发,调用scan函数,在scan函数中从(1,7)出发,扫描5所在的那个数据区域,并且将扫描过后的元素全部赋值为-1,代表该区域已经被访问过了,细胞计数器的值+1,此时数据变为:
第五次:按照上面的规律,不断在主函数中遍历元素,如果当前元素的数值落在1-9之间,那么就从这个元素出发,遍历这个元素所在的数字区域,并且将这个数字区域中的数值全部赋值为非1-9之间的数,那么最终,主函数中scan函数调用了几次,细胞数就会统计到几个。
三 核心代码
//用来存放数据 int a[65][65]; int m,n,sum=0; struct Point { int x; int y; }; queue<struct Point> q; void scan(int i,int j) { int k,ni,nj; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; struct Point p; p.x=i; p.y=j; q.push(p); while(!q.empty()) { p=q.front(); q.pop(); i=p.x; j=p.y; if(a[i][j]>=1&&a[i][j]<=9) //数值为1-9代表没有访问过,访问过的点无需再进行重复的扩展 { a[i][j]=-1; //给-1代表已经访问过 //遍历上下左右四个方向 for(k=0;k<=3;k++) { ni=i+dx[k]; nj=j+dy[k]; //前面两个控制越界,访问过的不再访问,元素为0也不能放进去,放进去的只能是1-9之间的元素 if(1<=ni&&ni<=m&&1<=nj&&nj<=n&&(a[ni][nj]!=-1&&a[ni][nj]!=0)) { p.x=ni; p.y=nj; q.push(p); } } } } sum++; }
四 完整代码
#include<bits/stdc++.h> using namespace std; //用来存放数据 int a[65][65]; int m,n,sum=0; struct Point { int x; int y; }; queue<struct Point> q; void scan(int i,int j) { int k,ni,nj; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; struct Point p; p.x=i; p.y=j; q.push(p); while(!q.empty()) { p=q.front(); q.pop(); i=p.x; j=p.y; if(a[i][j]>=1&&a[i][j]<=9) //数值为1-9代表没有访问过,访问过的点无需再进行重复的扩展 { a[i][j]=-1; //给-1代表已经访问过 //遍历上下左右四个方向 for(k=0;k<=3;k++) { ni=i+dx[k]; nj=j+dy[k]; //前面两个控制越界,访问过的不再访问,元素为0也不能放进去,放进去的只能是1-9之间的元素 if(1<=ni&&ni<=m&&1<=nj&&nj<=n&&(a[ni][nj]!=-1&&a[ni][nj]!=0)) { p.x=ni; p.y=nj; q.push(p); } } } } sum++; } int main() { //freopen("cell.in","r",stdin); //别忘记,文件里面放的数据没有用空格隔开 cin>>m>>n; char q[65][65]; for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { cin>>q[i][j]; a[i][j]=q[i][j]-'0'; } } for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { if(a[i][j]>=1&&a[i][j]<=9) { scan(i,j); } } } cout<<sum; return 0; }
五 注意点
1.遍历过后的数据区域要记得重新赋值为非1-9之间的数值
2.要去遍历数据数组中的每一个元素,不能够只遍历边缘或者某一部分数据,如下图:如果你只遍历边缘元素,那么按照上面的思路,最终程序的结果只会输出0,并不会得到预想的结果1,因为如果只遍历边缘,那么scan函数一次都不会被执行,这就错了!!!
3.计数器记得初始化为0
4.输入的时候记得对输入数据进行处理,看下图,输入的时候,数据区域部分是字符型的数据,需要将字符型的数据转换成整型数据
代码如下:
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐















所有评论(0)