bfs广度优先搜索

主要特点:BFS 是一种基于“层次”的搜索算法,适用于无权图的最短路径问题和连通性问题。它的实现依赖于队列,每次用队首转移状态,把新状态加入队尾,直至队列为空

题目:迷宫寻路     洛谷P3625(是之前分享过的一道题目,现在用bfs做)

 思路:

1.使用一个队列来存储待访问的节点,使用一个数组来记录已访问的节点,避免重复访问。将起点加入队列,并标记为已访问。

2.搜索。当队列不为空时,从队列中取出一个节点,作为当前节点。如果当前节点是目标节点,则搜索成功,返回结果。否则继续检查当前节点的所有相邻节点。如果相邻节点未被访问过,则将其加入队列,并标记为已访问。如果队列为空且未找到目标节点,则表示无法到达目标节点,搜索失败。

代码:

#include<iostream>
#include<queue>
using namespace std;
int n,m,flag;
int dx[]={-1,1,0,0},dy[]={0,0,1,-1},vis[110][110]; //也可以直接通过a[i][j]='#'标记访问 
char a[110][110];
void bfs(int x,int y){
	queue<pair<int,int> >q;     //q的元素类型也设成可以是结构体 
	q.push({x,y});         //使用队列q存储当前搜索的节点  
	vis[x][y]=1;   //标记已经访问过的节点,以避免重复访问同一个节点
	while(!q.empty()){  
		pair<int,int>t=q.front();
		if(t.first==n&&t.second==m){   //在搜索过程中到达终点,标记,退出 
			flag=1;
			return;
		} 
		q.pop();    //每次从队列中取出队首,检查其上下左右的相邻节点
		for(int i=0;i<4;i++){
			int xx=t.first+dx[i],yy=t.second+dy[i];
			if(xx<1||xx>n||yy<1||yy>m||a[xx][yy]=='#'||vis[xx][yy]){
				continue;     //越界||是障碍物||访问过了就不搜索 
			}
			q.push({xx,yy});  //加入队列 
			vis[xx][yy]=1;    //标记访问 
		}
	}
}
int main(){
	int i,j;
	cin>>n>>m;
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	bfs(1,1);      //从1,1开始搜索 
	if(flag) cout<<"Yes";
	else cout<<"No";
	return 0;
}

 bfs也适合用来求无权图的最短路径

题目:离开中山路 洛谷P1746

分享一下我开始的错误代码{ _ },我的s是在每次从队列中取出一个节点时增加。而BFS 的路径长度应该基于层级来计算,即每一层的节点共享相同的路径长度,最好使用一个dis数组存储。

#include<iostream>
#include<queue>
using namespace std;
int n,x2,y2,s;
int dx[]={-1,1,0,0},dy[]={0,0,1,-1}; 
char a[1010][1010];
void bfs(int x,int y){
	queue<pair<int,int> >q;     
	q.push({x,y});  
	a[x][y]='1';     //把访问过的节点置'1',避免重复访问 
	while(!q.empty()){  
		pair<int,int>t=q.front();
		if(t.first==x2&&t.second==y2){  //到达终点,退出 
			return;
		} 
		s++;    //没到终点,距离+1 
		q.pop();     
		for(int i=0;i<4;i++){
			int xx=t.first+dx[i],yy=t.second+dy[i];
			if(xx<1||xx>n||yy<1||yy>n||a[xx][yy]=='1'){
				continue;     
			}
			q.push({xx,yy});  //加入队列 
			a[xx][yy]='1';
		}
	}
}
int main(){
	int x1,y1,i,j;
	cin>>n;
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	cin>>x1>>y1>>x2>>y2;
	bfs(x1,y1);     
	cout<<s; 
	return 0;
}

正确版本      (我的代码也是不成熟的啊,大家有想法可以多多交流!)

#include<iostream>
#include<queue>
using namespace std;
int n,x2,y2;
int dx[]={-1,1,0,0},dy[]={0,0,1,-1}; 
int dis[1010][1010];     //记录每个点的最短路径长度 
char a[1010][1010];
void bfs(int x,int y){
	queue<pair<int,int> >q;     
	q.push({x,y});  
	a[x][y]='1';     //把访问过的节点置'1',避免重复访问 
	dis[x][y]=0;     //起始路径长度为0 
	while(!q.empty()){  
		pair<int,int>t=q.front();
		if(t.first==x2&&t.second==y2){  //到达终点,退出 
			return;
		} 
		q.pop();     
		for(int i=0;i<4;i++){
			int xx=t.first+dx[i],yy=t.second+dy[i];
			if(xx<1||xx>n||yy<1||yy>n||a[xx][yy]=='1'){
				continue;     
			}
			q.push({xx,yy});  //加入队列 
			dis[xx][yy]=dis[t.first][t.second]+1;  //前一点的距离+1 
			a[xx][yy]='1';
		}
	}
}
int main(){
	int x1,y1,i,j;
	cin>>n;
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	cin>>x1>>y1>>x2>>y2;
	bfs(x1,y1);     
	cout<<dis[x2][y2]; 
	return 0;
}

再分享一道plus版的题 

题目:迷宫  NC15136

输出是 20

思路:这是道有特殊规则的搜索题,但我们可以把它分成两种情况来简化判断

1.不拿钥匙,不经过门的最短路径

2.先去拿钥匙,再从钥匙到终点(可经过门)的最短路径

再判断2种情况能否到终点,取最小值即可。

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int h,w;
char a[510][510]; 
int dis[510][510],dx[]={-1,1,0,0},dy[]={0,0,-1,1};
pair<int,int>s,e,d,k,t;
int bfs(pair<int,int>st,pair<int,int>en){    //参数为2个点,搜索的起点、终点 
	queue<pair<int,int> >q;
	q.push(st);
	memset(dis,-1,sizeof(dis));        //初始化dis数组 
	dis[st.first][st.second]=0;        
	while(!q.empty()){
		t=q.front();
		q.pop();
		if(t==en){             //找到终点,返回最短距离 
			return dis[en.first][en.second];  
		}
		for(int i=0;i<4;i++){         //搜索 
			int xx=t.first+dx[i],yy=t.second+dy[i];
			if(xx<1||xx>h||yy<1||yy>w||a[xx][yy]=='W'||dis[xx][yy]!=-1||st==s&&a[xx][yy]=='D'){
				continue;  //越界||遇到墙||访问过||从s开始找且遇到门(我3次调用函数中,s1与s2不能经过门) 
			}
			q.push({xx,yy});       
			dis[xx][yy]=dis[t.first][t.second]+1;    //更新距离 
		}
	}
    return -1;    //注意(开始我漏了),int类型的函数要有返回值,如果队为空且没找到终点返回-1 
}
int main(){
	int i,j;
	cin>>h>>w;
	for(i=1;i<=h;i++){
		for(j=1;j<=w;j++){
			cin>>a[i][j];
			if(a[i][j]=='S') s=make_pair(i,j);
			if(a[i][j]=='E') e=make_pair(i,j);
			if(a[i][j]=='D') d=make_pair(i,j);
			if(a[i][j]=='K') k=make_pair(i,j);
		}
	}
	int s1=bfs(s,e);        //起点到终点 
	int s2=bfs(s,k);        //起点到钥匙 
	int s3=bfs(k,e);        //钥匙到终点 
	if(s1==-1&&(s2==-1||s3==-1)){       //两种情况都走不到终点 
		cout<<-1;
	}else if(s1==-1){                //第一种情况不行 
		cout<<s2+s3;
	}else{                            //两种情况都行,取最短距离 
		cout<<min(s1,s2+s3);	
	}
	return 0;
}

再来给大家分享一道三维数组的题,这应该是我第一次遇到,也把结构体的板子分享个给大家^.^ (只是两个变量时我个人喜欢用pair) 

题目:Jelly   NC201613

题目描述

 思路点拨:二维数组可以看作是我们的屏幕,由长宽组成,那么三维数组可以当作是再增加厚度的维度(一层一层叠起来,图emm意会一下吧),其它步骤都一样,就是求最短路径,直接放代码啦(大家所有文章最好是看一两道,然后后面就自己写,自己思考过了再来看我的代码o)

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int n;
char a[105][105][105];
int dis[105][105][105],dx[]={-1,1,0,0,0,0},dy[]={0,0,-1,1,0,0},dz[]={0,0,0,0,1,-1}; 
struct gd{
	int z,x,y;
};
void bfs(){
	queue<gd>q;
	q.push({1,1,1});    //这里我没通过传参了,题目给出(1,1,1),再设有点冗余 
	dis[1][1][1]=1;     //开始吃了1个果冻别忘了 
	while(!q.empty()){
		gd t=q.front();
		q.pop();
		if(t.x==n&&t.y==n&&t.z==n){
			return;
		}
		for(int i=0;i<6;i++){          //这里是以我理解的题目的层确定x,y,z的 
			int xx=t.x+dx[i],yy=t.y+dy[i],zz=t.z+dz[i];
			if(xx<1||xx>n||yy<1||yy>n||zz<1||zz>n||a[zz][xx][yy]=='*'||dis[zz][xx][yy]!=-1){
				continue;
			}
			q.push({zz,xx,yy});
			dis[zz][xx][yy]=dis[t.z][t.x][t.y]+1; 
		}
	} 
}	
int main(){
	int i,j,k;
	cin>>n;
	memset(dis,-1,sizeof(dis));
	for(k=1;k<=n;k++){     //第一层 
		for(i=1;i<=n;i++){
			for(j=1;j<=n;j++){
				cin>>a[k][i][j];
			}
		}
	}
	bfs();
	cout<<dis[n][n][n]; 
	return 0;
} 

 代码有很多写法呀大家,可能我也有不精简的地方,多种答案都合适哦,自己写完放到原oj上测试一下就好了,有好的想法也欢迎大家多多交流。

今日小tip:不知道大家有没有遇到这种情况,比如在main()函数里定义了int a[10000000],运行代码后就会自动结束了(运行故障),而放到函数外,定义为全局变量,代码就能正常运行了。这是因为局部变量在电脑中其实是存储在内存有限的栈上的,开辟的空间不能过大。设置为全局变量的好处一个是可以直接操作(不用调用),还有个就是内存大

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值