赞
踩
目录
BFS 全称是 Breadth First Search,中文名是宽度优先搜索,也叫广度优先搜索。
是图上最基础、最重要的搜索算法之一。
所谓宽度优先。就是每次都尝试访问同一层的节点。 如果同一层都访问完了,再访问下一层。
这样做的结果是,BFS 算法找到的路径是从起点开始的 最短 合法路径。换言之,这条路径所包含的边数最小。
在 BFS 结束时,每个节点都是通过从起点到该点的最短路径访问的。
算法过程可以看做是图上火苗传播的过程:最开始只有起点着火了,在每一时刻,有火的节点都向它相邻的所有节点传播火苗。
说点人话环节(doge):比如走迷宫,我们一开始肯定会有一个起始位置,而之后改如何走是不太确定的,因为有四个方向可以走,广搜就是,一次搜索就走这四个方向,搜过的点就标记一下,下次不要再回去找(所以这里会标记起始的点,而由起始点走出的四个点还没有搜索,所以不必标记)。之后再从走出的四个点分别进行搜索接下去的四个方向的点。由一次搜索走出来的所有点就是一层。在每次走完一层之后,就先判定一下这一层的点有没有所需要的答案,如果有,那么就不需要再找了。
在图上就是,以V1为起点,第一次搜索就搜V2,V3,并将V1标记。此为一层,这时,应该先判断一下V2,V3是不是我们所需要的点,如果不是,那么就继续,往下搜索V4,V5,V6,V7. 直到找到目标。
那这时候就有人要问了:那找不到怎么办?也有走不通的迷宫啊!
回答这个问题之前,我们要先想想,bfs搜索的时候,数据要放到哪里?我们想想,每一次搜索的时候是不是就是先将要搜索的点放入储存的结构中,然后搜索完标记并退出。这符合“先进先出”的特点,也就是队列的特点。由于只会c语言我们只能去模拟队列的实现。如何模拟呢?用一个数组和两个指针变量就可以了。
struct node queque[10000];
memset(queque,0,sizeof(queque));
int head=0,end=1;
就可以模拟队列的实现,当然,如果要使用模拟循环队列也是可以的。(记得加个判断,end>数组长度时,重置即可)但是要主要开好足够的空间 ,不然可能还没搜索,那一层的部分数据就会被覆盖掉。
因为一层一层搜索,所以如果还能走,必然有数据入队,自然就不会出现head>=end的情况(如果是循环队列,那么判断条件应该是head!=end,即head不会与end相等)。一旦出现上述情况,那么就说明没得搜了,已经都走遍了,如果此时还没找到答案,那么就是没有答案了。
目前我所遇到的基本上bfs的应用在于寻找最短路径类,之后如果有遇到其他的应用,会再写一篇文章来描述新的应用。
那么这里就以走迷宫为例,看看如何实现吧。
首先肯定要创建好结构体记录每次走的点,并创建好队列
struct node
{
int num;
int i;
int j;
};struct node queque[10000];
memset(queque,0,sizeof(queque));
int head=0,end=1;
然后创建走的四个方向
int local[][2]=
{
{0,1},
{0,-1},
{1,0},
{-1,0}
};
之后就是主体部分的实现了
int tmp=end;
while(head<tmp)//一层搜索
{
for(int i=0;i<4;i++)//每个点搜索四个方位
{
int tmpi=queque[head].i+local[i][0];
int tmpj=queque[head].j+local[i][1];
if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)//判断边界条件
{
continue;
}
else if(arr[tmpi][tmpj]==-1)//搜索过的或者不可走点
{
continue;
}
else
{
//存下数值和地址,并将其入队
queque[end].num=arr[tmpi][tmpj];
queque[end].i=tmpi;
queque[end].j=tmpj;
end++;
arr[tmpi][tmpj]=-1;
}
}
head++;
}
c大楼有一个一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼 (1≤i≤N) 上有一个数字Ki(0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3,3,1,2,5 代表了Ki (K1=3,K2=3,…),在1楼,按“上”可以到4楼,按“下”是不起作用的,因为没有−2楼。那么,从A楼到B楼至少要按几次按钮呢?
输入格式:
第一行包含3个用空格隔开的正整数,分别表示N,A,B (1≤N≤200,1≤A,B≤N) 。 第二行包含N 个用空格隔开的非负整数,表示Ki 。
输出格式:
输出共一行,即最少按键次数,若无法到达,则输出 −1 。
输入样例:
5 1 5 3 3 1 2 5输出样例:
3
这道题也是典型bfs的题目,具体的实现即解释也在代码中
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- int judge(int arr[])
- {
- int i=0;
- for(i=0;i<2000;i++)
- {
- if(arr[i]!=0)
- return 1;
- }
- return 0;
- }
- int main()
- {
- int n;
- scanf("%d",&n);
- int arr[n+1];
- int i=0;
- int sta,end;
- scanf("%d%d",&sta,&end);
- for(i=1;i<=n;i++)
- {
- scanf("%d",&arr[i]);
- }
- arr[0]=0;
- int queque[2000];//模拟队列的实现
- memset(queque,0,sizeof(queque));
- int top=0,bottom=1;
- queque[top]=sta;
- int count=0;
- int flag=0;
- while(judge(queque))
- {
- //广搜对一层的搜索
- int tmp=bottom;
- while(top<tmp)
- {
- //此处节点标记采用arr[i]=0方式
- if(arr[queque[top]]!=0)
- {
- if(queque[top]+arr[queque[top]]<=n)
- {
- //对上楼层节点进行访问,将可访问节点放入队列并标记该节点
- queque[bottom++]=queque[top]+arr[queque[top]];
- //arr[queque[top]+arr[queque[top]]]=0;
- }
- if(queque[top]-arr[queque[top]]>0)
- {
- //对下楼层进行访问,将可访问节点放入队列并标记该节点
- queque[bottom++]=queque[top]-arr[queque[top]];
- }
- arr[queque[top]]=0;
- }
- top++;
- }
- //搜索一次就需要按一次电梯
- count++;
- //判断目前的层数中有无目标数
- int i=0;
- for(;i<top;i++)
- {
- if(queque[i]==end)
- {
- flag=1;
- goto ending;
- }
- queque[i]=0;
- }
- }
- ending:
- if(flag)
- printf("%d",count-1);
- else
- printf("-1");
- return 0;
- }
奕哥今天玩到一款寻宝游戏,地图是一个n*m的矩阵,其中分布着一些宝藏,每个宝藏具有一定的价值,奕哥只能拿走其中一个宝藏。奕哥起始在a行b列。奕哥可以向相邻的一格移动,但不能走出地图外。奕哥初始体力值X,移动一格需要消耗体力值为1。体力耗尽后奕哥无法继续移动。地图中有一些障碍物,奕哥无法移动到障碍物上。奕哥想知道他能拿到的最具价值的宝藏是哪一个。
输入格式:
第一行包含5个整数n,m,a,b,X。n,m分别表示地图行数,列数;a,b表示奕哥起始位置在a行b列;X表示奕哥的体力。
( 1<=n,m<=1000, 1<=a<=n, 1<=b<=m, 0<=X<=1e10)
接下来n行,每行m个整数。第i行第j个整数为Aij (-1<=Aij<=1e9)。若Aij=-1,则表示第i行第j列有障碍物;否则表示该位置有一个价值为Aij的宝物。保证起始位置没有障碍物。
输出格式:
奕哥能拿到价值最高的宝藏的价值。
输入样例:
3 3 1 1 3 0 -1 10 3 5 7 -1 8 15输出样例:
8
这就是迷宫题了,上面的点已经说得清楚了,不懂的可以再上去看看
- #include<stdio.h>
- #include<string.h>
- int local[][2]=
- {
- {0,1},
- {0,-1},
- {1,0},
- {-1,0}
- };
- struct node
- {
- int num;
- int i;
- int j;
- };
- int main()
- {
- int n, m, a, b, x;
- scanf("%d%d%d%d%d", &n, &m, &a, &b, &x);
- a--;//数组中初始位置要减一
- b--;
- int arr[n][m];//读入地图
- int i=0;
- for (i = 0; i<n; i++)
- {
- int j = 0;
- for (j = 0; j<m; j++)
- {
- scanf("%d", &arr[i][j]);
- }
- }
- struct node queque[10000];
- memset(queque,0,sizeof(queque));
- int head=0,end=1;
- queque[head].num=arr[a][b];
- queque[head].i=a;//记录对应数值的坐标
- queque[head].j=b;
- arr[a][b]=-1;//搜索过的就直接标记
- while(x--)
- {
- int tmp=end;
- while(head<tmp)//一层搜索
- {
- for(int i=0;i<4;i++)//每个点搜索四个方位
- {
- int tmpi=queque[head].i+local[i][0];
- int tmpj=queque[head].j+local[i][1];
- if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)//判断边界条件
- {
- continue;
- }
- else if(arr[tmpi][tmpj]==-1)//搜索过的或者不可走点
- {
- continue;
- }
- else
- {
- //存下数值和地址,并将其入队
- queque[end].num=arr[tmpi][tmpj];
- queque[end].i=tmpi;
- queque[end].j=tmpj;
- end++;
- arr[tmpi][tmpj]=-1;
- }
- }
- head++;
- }
- }
- int max=0;
- for(int i=0;i<end;i++)
- {
- if(queque[i].num>max)
- max=queque[i].num;
- }
- printf("%d",max);
- return 0;
- }
有一个大小为n*m的矩形小镇,城镇上有房屋(“#”表示无法通过),有空地(“.”表示可通行),每次移动只能朝上下左右四个方向,且需花费1单位时间。
一天,二乔和承太郎约定在龙舌兰酒吧见面,两人同时从各自所在位置向酒吧出发。请问最少需要过多少时间他们才能在酒吧碰面。
地图上P表示二乔的位置,W表示承太郎的位置,B表示酒吧的位置。
输入格式:
第一行包含两个整数n,m,表示城镇的大小。(1<=m,n<=1000)。
接下来n行,每行m个字符,表示小镇的情况。
输出格式:
输出两人在酒吧碰面的最少花费时间,如果无法在酒吧碰面则输出-1。
输入样例:
5 4 .W.# P#.. .... B..# #...输出样例:
4
具体代码实现如下:
- #include<stdio.h>
- #include<string.h>
- //四个方位
- int local[][2]={
- {0,1},
- {0,-1},
- {1,0},
- {-1,0}
- };
- struct node
- {
- char ch;
- int i;
- int j;
- };
- int judge(struct node *queque,int n)
- {
- int i=0;
- for(;i<n;i++)
- {
- if(queque[i].ch!=0)
- return 1;
- }
- return 0;
- }
- int main()
- {
- int n,m;
- scanf("%d%d",&n,&m);
- char arrp[n][m];//供p寻找
- char arrw[n][m];//供w寻找
- struct node queque[600000];//模拟队列实现
- memset(queque,0,sizeof(queque));
- for(int i=0;i<n;i++)
- {
- getchar();
- for(int j=0;j<m;j++)
- {
- scanf("%c",&arrp[i][j]);
- }
- }
- for(int i=0;i<n;i++)
- {
- for(int j=0;j<m;j++)
- arrw[i][j]=arrp[i][j];
- }
- int pi,pj,wi,wj,tari,tarj;
- for(int i=0;i<n;i++)
- {
- for(int j=0;j<m;j++)
- {
- if(arrp[i][j]=='P')//找到P坐标
- {
- pi=i;
- pj=j;
- }
- if(arrp[i][j]=='W')//找到W坐标
- {
- wi=i;
- wj=j;
- }
- }
- }
-
- /*
- 此处为测试arrp和arrw是否正常
- for(int i=0;i<n;i++)
- {
- for(int j=0;j<m;j++)
- {
- printf("%c",arrw[i][j]);
- }
- printf("\n");
- }
- */
- int countp=0;
- int countw=0;
- int flag1=0;
- //此处开始为两次bfs的实现
- //1.arrp的路径寻找
- int head=0;
- int end=1;
- queque[head].ch=arrp[pi][pj];
- queque[head].i=pi;
- queque[head].j=pj;
- while(judge(queque,end))//判断队列是否为空
- {
- int tmp=end;
- while(head<tmp)
- {
- for(int i=0;i<4;i++)
- {
- int tmpi=queque[head].i+local[i][0];
- int tmpj=queque[head].j+local[i][1];
- if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)
- continue;
- else if(arrp[tmpi][tmpj]=='#')
- continue;
- else
- {
- queque[end].ch=arrp[tmpi][tmpj];
- queque[end].i=tmpi;
- queque[end].j=tmpj;
- arrp[tmpi][tmpj]='#';
- end++;
- }
- }
- queque[head].ch=0;//搜索完出队列
- head++;
- }
- countp++;//搜索完一次走一步
- for(int i=head;i<end;i++)
- {
- if(queque[i].ch=='B')
- {
- flag1=1;
- break;
- }
- }
- if(flag1)
- break;
- }
- memset(queque,0,sizeof(queque));
- head=0;
- end=1;
- queque[head].ch=arrw[wi][wj];
- queque[head].i=wi;
- queque[head].j=wj;
- //2.arrw路径寻找
- int flag2=0;
- while(judge(queque,end))//判断队列是否为空
- {
- int tmp=end;
- while(head<tmp)
- {
- for(int i=0;i<4;i++)
- {
- int tmpi=queque[head].i+local[i][0];
- int tmpj=queque[head].j+local[i][1];
- if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)
- continue;
- else if(arrw[tmpi][tmpj]=='#')
- continue;
- else
- {
- queque[end].ch=arrw[tmpi][tmpj];
- queque[end].i=tmpi;
- queque[end].j=tmpj;
- arrw[tmpi][tmpj]='#';
- end++;
- }
- }
- queque[head].ch=0;
- head++;
- }
- countw++;
- for(int i=head;i<end;i++)
- {
- if(queque[i].ch=='B')
- {
- flag2=1;
- break;
- }
- }
- if(flag2)
- break;
- }
- if(flag1==0||flag2==0)
- printf("-1");
- else
- {
- printf("%d",countp>countw?countp:countw);
- }
- return 0;
- }
总体来说,bfs理解了就会很难(doge),bfs也是图论的一个基础算法,希望这篇文章能帮助到你掌握bfs,看到这里不妨给我点个赞吧。之后大概就是一周一篇了。要多输入,才能有输出嘛,多数时间还是要拿来学习的。(还是太菜)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。