搜索专题之BFS

BFS一般用队列实现,且总是按层次的顺序进行的遍历,其基本写法如下:

void BFS(int s){
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        取出队首元素top;
        访问队首元素top;
        队首元素出队;
        将下一层结点中未访问过的结点全部入队,并设置成以访问; 
        //一般是枚举若干个方向,判断合法性,若合法刷新坐标,标记以访问,然后入队
    }
}

上面需要用到:

1)坐标的描述 ( 以二维为例 )

struct node{
	int x,y;
        int step;//记录步数
} Node;

若想记录步数,只需在刷新坐标的同时队 对应step+1 即可

2)枚举若干方向

int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};

3)判断坐标合法性

bool Judge(int x,int y){
	if(x>=n || x<0 || y>=m || y<0) return false;
	if(vis[x][y]==true || matrix[x][y]==0) return false;
	return true;
} 

由于每一个BFS可以访问一个联通块,

因此,若想得到联通块的个数只需在调用BFS函数后进行变量++记录即可。若想得到每一个联通块内的符合条件的的坐标的个数(比如0 1矩阵中1的个数),只需在BFS函数内部队首元素出队访问时进行变量++记录即可。

几个练习题小试牛刀:

1 :求矩阵中 “块“ 的个数(块为若干个1相邻)

输入数据:

6 7
0 1 1 1 0 0 1
0 0 1 0 0 0 0
0 0 0 0 1 0 0
0 0 0 1 1 1 0
1 1 1 0 1 0 0
1 1 1 1 0 0 0

输出数据

4

完整代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
struct node{
	int x,y;
} Node;

int m,n;
int matrix[maxn][maxn];
bool vis[maxn][maxn];
//枚举四个方向;
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};

bool Judge(int x,int y){
	if(x>=n || x<0 || y>=m || y<0) return false;
	if(vis[x][y]==true || matrix[x][y]==0) return false;
	return true;
} 

void BFS(int x,int y){
	queue<node> q;
	Node.x =x,Node.y =y; //当前坐标 
	q.push(Node);
	vis[x][y]=true;  //设置(x,y)已入队过 
	while(!q.empty()){
		node now=q.front();
		q.pop();
		//枚举四个方向
		for(int i=0;i<4;i++){
			int NewX=now.x +X[i];
			int NewY=now.y +Y[i];
			if(Judge(NewX,NewY)){ // 
				Node.x =NewX;
				Node.y =NewY;
				vis[NewX][NewY]=true;
				q.push(Node);
			}
		}
	}
}
			
int main(){ 
	cin>>n>>m;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>matrix[i][j]; 
		}
	}
	int ans=0;//存块数
	//枚举每一个位置;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			if(matrix[i][j]==1 && vis[i][j]==false){
				BFS(i,j);
				ans++;
			} 
        }
    }
    cout<<ans<<endl;
    return 0;
}

2 :求走迷宫的最小步数

由S到达T的最小步数,' * '代表墙,不可穿过,‘ . ’   可以走

输入数据:

5 5
.....
.*.*.
.*S*.
.***.
...T*
2 2 4 3

输出数据:
 

11

思路:个上一道题类似,只是在BFS搜索中添加了步数,也就是结构体node里添加了step记录步数而已,对上述代码进行简单修改即可。

完整代码:
 

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
struct node{
	int x,y;
	int step; 
} S,T,Node;

int m,n;
char matrix[maxn][maxn];
bool vis[maxn][maxn];
//枚举四个方向;
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};

bool Judge(int x,int y){
	if(x>=n || x<0 || y>=m || y<0) return false;
	if(vis[x][y]==true || matrix[x][y]=='*') return false;
	return true;
} 

int BFS(int x,int y){
	queue<node> q;
	q.push(S);
	vis[x][y]=true;  //设置(x,y)已入队过 
	while(!q.empty()){
		node now=q.front();
		q.pop();
		if(now.x == T.x  && now.y == T.y ){ //起点是终点 
			return now.step ; 
		}
		//枚举四个方向
		for(int i=0;i<4;i++){
			int NewX=now.x +X[i];
			int NewY=now.y +Y[i];
			if(Judge(NewX,NewY)){ // 
				Node.x =NewX;
				Node.y =NewY;
				Node.step =now.step +1;
				vis[NewX][NewY]=true;
				q.push(Node);
			}
		}
	}
	return -1;
}
			
int main(){ 
	cin>>n>>m;
	for(int i=0;i<n;i++){
		getchar();// 过滤换行符
		for(int j=0;j<m;j++){
			cin>>matrix[i][j]; 
		}
	}
	cin>>S.x >>S.y >>T.x >>T.y ;
    cout<<BFS(S.x,S.y)<<endl;
    return 0;
}
	

3:1091 Acute Stroke (30 point(s))

这道题也是个典型的BFS搜索,不过是三维的: 

注意点:1) 数据读入时先读入第三维(切片的个数),再读入每一片(测试样例也很容易看到)

               2)在访问每一块中记录1的个数(每访问一个块,返回一个块内1的个数)

根据前面的模板,很容易写出代码:
完整代码:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int x,y,z;
} Node;

int m,n,l,t; 
int matrix[1300][130][80];
bool vis[1300][130][80];
//枚举八个方向;
int X[6]={1,-1,0,0,0,0};
int Y[6]={0,0,1,-1,0,0};
int Z[6]={0,0,0,0,1,-1};

bool Judge(int x,int y,int z){
	if(x>=n || x<0 || y>=m || y<0 || z>=l || z<0) return false;
	if(vis[x][y][z]==true || matrix[x][y][z]==0) return false;
	return true;
} 

int BFS(int x,int y,int z){
	queue<node> q;
	Node.x =x; //当前坐标
	Node.y =y;
	Node.z =z; 
	int cnt=0; //记录联通块中 1 的个数 
	q.push(Node);
	vis[x][y][z]=true;  //设置(x,y)已入队过 
	while(!q.empty()){
		node now=q.front();
		q.pop();
		cnt++;//当前块中1的个数 +1 
		for(int i=0;i<6;i++){
			int NewX=now.x +X[i];
			int NewY=now.y +Y[i];
			int NewZ=now.z +Z[i];
			if(Judge(NewX,NewY,NewZ)){ // 
				Node.x =NewX;
				Node.y =NewY;
				Node.z =NewZ;
				vis[NewX][NewY][NewZ]=true;
				q.push(Node);
			}
		}
	}
	if(cnt>=t) return cnt; //如果超过阈值,即这个联通块中 1 的个数不小于 t 才算数。 
	else return 0;
}
			
int main(){   
	cin>>n>>m>>l>>t;
	for(int z=0;z<l;z++){ //先输入层数,即第三维 
		for(int x=0;x<n;x++){
			for(int y=0;y<m;y++){
				cin>>matrix[x][y][z];  //注意数据读入 
			} 
		}
	}
	int ans=0; 
	//枚举每一个位置;
	for(int z=0;z<l;z++){  //先输入层数,即第三维 
		for(int x=0;x<n;x++){
			for(int y=0;y<m;y++){
			    if(matrix[x][y][z]==1 && vis[x][y][z]==false){
				   ans+=BFS(x,y,z);	
			    } 
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

以上!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZCAIHUI_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值