主要特点: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],运行代码后就会自动结束了(运行故障),而放到函数外,定义为全局变量,代码就能正常运行了。这是因为局部变量在电脑中其实是存储在内存有限的栈上的,开辟的空间不能过大。设置为全局变量的好处一个是可以直接操作(不用调用),还有个就是内存大