题外话
我们最近学了BFS,老师让我们写一篇博客来巩固知识点,(本人第一次写博客,如有做的不好的地方请在评论区内指出,请各位大佬多多包涵)
BFS简介
(以下摘自百度百科)毕竟本人不会(doge
宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
看上去讲了很多,不用特意去记,但你只要知道这是一个图的搜索算法之一就行了。
BFS算法实现过程
BFS算法流程框架
咳咳,接下来我会手把手的教你如何去写BFS算法,但毕竟空讲也没什么用所以我会用一道图让大家理解大致流程。
再次用用百度百科相关解释:
已知图G=(V,E)和一个源顶点s,宽度优先搜索以一种系统的方式探寻G的边,从而“发现”s所能到达的所有顶点,并计算s到所有这些顶点的距离(最少边数),该算法同时能生成一棵根为s且包括所有可达顶点的宽度优先树。对从s可达的任意顶点v,宽度优先树中从s到v的路径对应于图G中从s到v的最短路径,即包含最小边数的路径。该算法对有向图和无向图同样适用。
之所以称之为宽度优先算法,是因为算法自始至终一直通过已找到和未找到顶点之间的边界向外扩展,就是说,算法首先搜索和s距离为k的所有顶点,然后再去搜索和S距离为k+l的其他顶点。
为了保持搜索的轨迹,宽度优先搜索为每个顶点着色:白色、灰色或黑色。算法开始前所有顶点都是白色,随着搜索的进行,各顶点会逐渐变成灰色,然后成为黑色。在搜索中第一次碰到一顶点时,我们说该顶点被发现,此时该顶点变为非白色顶点。因此,灰色和黑色顶点都已被发现,但是,宽度优先搜索算法对它们加以区分以保证搜索以宽度优先的方式执行。若(u,v)∈E且顶点u为黑色,那么顶点v要么是灰色,要么是黑色,就是说,所有和黑色顶点邻接的顶点都已被发现。灰色顶点可以与一些白色顶点相邻接,它们代表着已找到和未找到顶点之间的边界。
在宽度优先搜索过程中建立了一棵宽度优先树,起始时只包含根节点,即源顶点s.在扫描已发现顶点u的邻接表的过程中每发现一个白色顶点v,该顶点v及边(u,v)就被添加到树中。在宽度优先树中,我们称结点u 是结点v的先辈或父母结点。因为一个结点至多只能被发现一次,因此它最多只能有–个父母结点。相对根结点来说祖先和后裔关系的定义和通常一样:如果u处于树中从根s到结点v的路径中,那么u称为v的祖先,v是u的后裔。
看得懂吗,不过看不懂也没关系,流程图画出来后一切就变得简单明了了:
例题讲解
讲了这么多,没有实践那还不是啥也不知道,接下来就让我们做一道例题来实践一下:
最少步数
描述 这有一个迷宫,有0-8行和0-8列:
1,1,1,1,1,1,1,1,1
1,0,0,1,0,0,1,0,1
1,0,0,1,1,0,0,0,1
1,0,1,0,1,1,0,1,1
1,0,0,0,0,1,0,0,1
1,1,0,1,0,1,0,0,1
1,1,0,1,0,1,0,0,1
1,1,0,1,0,0,0,0,1
1,1,1,1,1,1,1,1,10表示道路,1表示墙。
现在输入一个道路的坐标作为起点,再如输入一个道路的坐标作为终点,问最少走几步才能从起点到达终点?
(注:一步是指从一坐标点走到其上下左右相邻坐标点,如:从(3,1)到(4,1)。)
输入描述
第一行输入一个整数n(0<n<=100),表示有n组测试数据;
随后n行,每行有四个整数a,b,c,d(0<=a,b,c,d<=8)分别表示起点的行、列,终点的行、列。输出描述
输出最少走几步。
用例输入 1
2 3 1 5 7 3 1 6 7
用例输出 1
12 11
首先呢导入必要的头文件基本操作,不用讲了吧
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
然后定义一下数据和结构体point(要是不知道结构体是啥的话,到这个网页挑一个和眼缘的教程看:传送门)
int fx[4] = { -1,1,0,0 }, fy[4] = { 0,0,-1,1 };
//fx表示在x轴上位置递增的量,fy同理
int ex,ey;//ex和ey表示终点的x坐标和y坐标
struct point
{
int x,y,step;
point(int x,int y,int step){
this->x=x;//x表示这个点x轴坐标
this->y=y;//y表示这个点y轴坐标
this->step=step;//step表示起点到这个点所经历的步骤数
}
};
接着,因为按照题目要求,要输入多组数据所以定义一个bfs函数(解释写注释里了)
/* 起点位置 地图 */
int bfs(int x,int y,int map[][9]){
queue<point> v;//定义一个队列用来存储将要走的节点
v.push(point(x,y,0));//将起点推入队列
while (!v.empty())//
{
point n=v.front();v.pop();//取出队列中第一个节点
if(n.x==ex && n.y==ey){//如果这个节点是终点的话,则返回起点到这个点的步数
return n.step;
}
for(int i=0;i<4;i++){//遍历方向数组
int nx=n.x+fx[i],ny=n.y+fy[i];//取出n的所有相邻节点
if(nx<9 && ny<9 && nx>=0 && ny>=0 && map[nx][ny]==0){//判断坐标是否合法
v.push(point(nx,ny,n.step+1));//合法的话就将这个的推入数组,并将距离起点的点数加1
map[nx][ny]=1;//讲这个点标记为已走过
}
}
}
}
接下来写主程序
int main(){
int x,y;
int t;
scanf("%d",&t);
while (t--){
int map[9][9] =
{ { 1,1,1,1,1,1,1,1,1 },
{ 1,0,0,1,0,0,1,0,1 },
{ 1,0,0,1,1,0,0,0,1 },
{ 1,0,1,0,1,1,0,1,1 },
{ 1,0,0,0,0,1,0,0,1 },
{ 1,1,0,1,0,1,0,0,1 },
{ 1,1,0,1,0,1,0,0,1 },
{ 1,1,0,1,0,0,0,0,1 },
{ 1,1,1,1,1,1,1,1,1 },
};//定义地图,地图不能用全局变量否则下一次输入的地图就不是原来的那个了
scanf("%d%d%d%d",&x,&y,&ex,&ey);//输入起点和终点坐标
printf("%d\n",bfs(x,y,map));//调用函数并输出结果
}
return 0;
}
最后附上完整AC代码:
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
int fx[4] = { -1,1,0,0 }, fy[4] = { 0,0,-1,1 };
int ex,ey;
struct point
{
int x,y,step;
point(int x,int y,int step){
this->x=x;
this->y=y;
this->step=step;
}
};
int bfs(int x,int y,int map[][9]){
queue<point> v;
v.push(point(x,y,0));
while (!v.empty())
{
point n=v.front();v.pop();
if(n.x==ex && n.y==ey){
return n.step;
}
for(int i=0;i<4;i++){
int nx=n.x+fx[i],ny=n.y+fy[i];
if(nx<9 && ny<9 && nx>=0 && ny>=0 && map[nx][ny]==0){
v.push(point(nx,ny,n.step+1));
map[nx][ny]=1;
}
}
}
}
int main(){
int x,y;
int t;
scanf("%d",&t);
while (t--){
int map[9][9] =
{ { 1,1,1,1,1,1,1,1,1 },
{ 1,0,0,1,0,0,1,0,1 },
{ 1,0,0,1,1,0,0,0,1 },
{ 1,0,1,0,1,1,0,1,1 },
{ 1,0,0,0,0,1,0,0,1 },
{ 1,1,0,1,0,1,0,0,1 },
{ 1,1,0,1,0,1,0,0,1 },
{ 1,1,0,1,0,0,0,0,1 },
{ 1,1,1,1,1,1,1,1,1 },
};
scanf("%d%d%d%d",&x,&y,&ex,&ey);
printf("%d\n",bfs(x,y,map));
}
return 0;
}