(本篇博客中的代码均是c语言代码,c++正在学......)
一.bfs算法的介绍。
资料中是这样解释bfs算法的。
宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
(简单来说,bfs是一种通过盲目搜索的方法,搜索图中的所有节点,找到最终答案。而不考虑结果的位置,完整的搜索整张图,直到找到结果为止。)
还有很多不同的理解如:
宽度优先搜索是一种对图进行搜索的算法。假设我们一开始位于某个顶点(即起点),此时并不知道图的整体结构,而我们的目的是从起点开始顺着边搜索,直到到达指定顶点(即终点)。在此过程中每走到一个顶点,就会判断一次它是否为终点。广度优先搜索会优先从离起点 近的顶点开始搜索。
https://blog.csdn.net/aliyonghang/article/details/128724989(原文摘自)
我们可以将bfs和dfs的过程图对比一下,可以比较两者的差别,也可以帮助我们理解bfs的搜索过程。(左边是dfs,右边的bfs)
我们难看出,bfs的搜索过程和队列具有相似性,因而我们常常通过队列的形式模拟bfs的搜索过程。
最后如果想要详细的学习bfs的搜索过程,以及掌握其使用方法可以看这篇博客
https://blog.csdn.net/aliyonghang/article/details/128724989
二.通过迷宫问题理解bfs的搜索过程。
之前我分析过dfs求解迷宫问题,但在这里,我就省个事,推送一个非常详细的视频,肯定比我讲的的要透彻很多(非常容易理解)。
三.例题分析。
一.马的遍历。
https://www.luogu.com.cn/problem/P1443
题目描述
有一个 n×m 的棋盘,在某个点 (x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
输入格式
输入只有一行四个整数,分别为 n,m,x,y。
输出格式
一个 n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 −1−1)。
输入输出样例
输入 #1复制
3 3 1 1
输出 #1复制
0 3 2 3 -1 1 2 1 4
说明/提示
对于全部的测试点,保证 1≤x≤n≤400,1≤y≤m≤400。
对于本题,其实没有什么难点,其实就是把棋盘上的每一个点按照规则入队,第一次到达该点时的步数一定是最优步数,唯一需要注意的点就是将马能到达的点用数组表示清楚就可以了。其他地方就和正常广搜没有差不多。
直接上代码吧(话都在代码里)
#include<stdio.h>
#include<string.h>
struct queue{ //通过结构体建立队列
int x;
int y;
};
int main()
{
int n,m,startx,starty;
scanf("%d %d %d %d",&n,&m,&startx,&starty);
int a[401][401]; //录入棋盘
int vis[401][401]={0}; //标记数组
memset(a,-1,sizeof(a)); //将所有点都设为-1,最后马不能到达的点就是-1的点
struct queue r[96000];
int head=1,tail=1;
r[tail].x=startx; //将起点入队
r[tail].y=starty;
tail++;
a[startx][starty]=0;
vis[startx][starty]=1; //将起点标记
int next1[8]={-2,-2,2,2,1,-1,1,-1}; //马能行动的位置,类似与方向数组
int next2[8]={-1,1,-1,1,2,-2,-2,2};
while(head<tail) //不为空队
{
for(int i=0;i<8;i++)
{
int dx=r[head].x+next1[i];
int dy=r[head].y+next2[i];
if(dx<1||dx>n||dy<1||dy>m) //边界条件
continue;
if(vis[dx][dy]==0)
{
r[tail].x=dx; //入队操作
r[tail].y=dy;
a[dx][dy]=a[r[head].x][r[head].y]+1;
vis[dx][dy]=1;
tail++;
}
}
head++; //不要忘记,搜索完后要出队
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}
二.Meteor Shower S。
https://www.luogu.com.cn/problem/P2895
题目描述
贝茜听说一场特别的流星雨即将到来:这些流星会撞向地球,并摧毁它们所撞击的任何东西。她为自己的安全感到焦虑,发誓要找到一个安全的地方(一个永远不会被流星摧毁的地方)。
如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。
根据预报,一共有 M 颗流星 (1≤M≤50,000) 会坠落在农场上,其中第 i 颗流星会在时刻 Ti(0≤Ti≤1000)砸在坐标为(Xi,Yi)(0≤Xi≤300,0≤Yi≤300) 的格子里。流星的力量会将它所在的格子,以及周围 44个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。
贝茜在时刻 0 开始行动,她只能在第一象限中,平行于坐标轴行动,每 1 个时刻中,她能移动到相邻的(一般是 4 个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 t 被流星撞击或烧焦,那么贝茜只能在 t 之前的时刻在这个格子里出现。 贝茜一开始在 (0,0)。
请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 −1。
输入格式
共 M+1 行,第 1 行输入一个整数 M,接下来的 M 行每行输入三个整数分别为 Xi,Yi,Ti。
输出格式
贝茜到达安全地点所需的最短时间,如果不可能,则为 −1。
输入输出样例
输入 #1复制
4 0 0 2 2 1 2 1 1 2 0 3 5
输出 #1复制
5
对于这题难道明显就上升了许多,(主要是坑点太多),如果单纯从解题思路来看,这题与迷宫问题差不多,但是本题加入了时间的判断,让我一开始拿到这题无从下手。其思路为:陨石地图用一个二维数组记录,内容为砸下时间,以时间早为标准;再用一个二维数组记录每个点最短时间;终止条件:如果搜到一个点永远不会被陨石砸到,输出该点时间,或者直到搜索结束也没有出去,输出-1。
注意点:
1.坐标不能低于0,但可以超300!2.流星定时砸下;3.流星砸下时间已最早的那个为准!4.如果出不去还要输出-1!
#include<stdio.h>
#include<string.h>
struct queue{ //常规建立队列
int x;
int y;
int time; //记录时间
};
int main()
{
int a[305][305],vis[305][305]={0}; //a数组存放地图,内容为陨石落下的时间
int n,x1,y1,t;
scanf("%d",&n);
int next1[5]={0,0,1,0,-1}; //方向数组
int next2[5]={0,1,0,-1,0};
memset(a,-1,sizeof(a)); //将数组中值全部赋值为-1,方便最后输出
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&x1,&y1,&t);
for(int j=0;j<5;j++)
{
if(x1+next1[j]>=0&&y1+next2[j]>=0&&(a[x1+next1[j]][y1+next2[j]]==-1||a[x1+next1[j]][y1+next2[j]]>t)) //如果该标记x,y坐标大于-1且该点没有被陨
a[x1+next1[j]][y1+next2[j]]=t; //石砸落或已标记时间没有该时间早
} //标记陨石
}
struct queue r[90005];
int head=1,tail=1;
r[tail].x=0; //将起点入队
r[tail].y=0;
r[tail].time=0;
tail++;
vis[0][0]=1;
while(head<tail) //下面的搜索代码都大差不差
{
for(int i=1;i<=4;i++)
{
int dx=r[head].x+next1[i];
int dy=r[head].y+next2[i];
if(dx<0||dy<0) //边界条件,能走到地图外
continue;
if(vis[dx][dy]==0&&(a[dx][dy]>r[head].time+1||a[dx][dy]==-1))
{
r[tail].x=dx;
r[tail].y=dy;
r[tail].time=r[head].time+1;
vis[dx][dy]=1;
if(a[dx][dy]==-1) //终止条件,找到安全地带
{
printf("%d\n",r[tail].time);
return 0;
}
tail++;
}
}
head++;
}
printf("-1\n"); //没有找到安全地带
return 0;
}
最后因为时间问题,例题分析就先到这里了 ,以后有机会再给大家分享。
四.bfs算法小结。
算法步骤
(1)把起始节点S线放到queue 表中克祥
(2)如果queue是空表,则失败退出,否则继续。
(3)在queue表中取最前面的节点node 移到 CL OSED 表中。(出队)
(4)扩展node节点。若没有后继即叶节点),则转向(2)循环。
(5)把node的所有后继节点放在queue表的末端。各后继结点指针指向node节点。(入队)
(6)若后继节点中某一个是目标节点,则找到一个解,成功退出。否则转向(2)循环
原文链接:https://blog.csdn.net/zgdlxs/article/details/122747226
最后,又到了我的宇宙免责声明 环节了:关于本篇博客是我(新手小白)对于bfs的算法的一些粗陋的认识,无论是认识还是代码中,肯定会有一些错误,欢迎大家指正,给予意见。如果本篇小结不符合您的认知,亦或者对本篇博客不满意,可以将其当做笑话。最后,希望和大家一起进步,一同努力。