跟DFS离不开的就是BFS(若不懂dfs请看我上篇博客DFS)
在我写完一篇关于DFS的博客后那么紧接着就要写一篇BFS的
1.基本概念
广度优先搜索(BFS全称Breadth First Search)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
2.基本思想
我们可以在每次出发的时候,走到离自己最近的点,由此我们每次都保证走最近的,那从局部最近推整体最近,必有一条路是整体最近的。所以我们可以利用BFS做最短路问题
(注:和某贪心算法“肥肠”类似)
因为同样难理解所以我也放一张图来演示一下
(注:相同颜色的箭头表示同一时间走的)
每次都在原来的基础上向外再走一步以达到寻找最短路径的目的,这也是BFS的核心思想体现
3.模板展示
int m[]={0,1,0,-1,0};
int v[]={0,0,1,0,-1};//依旧定义两个位置移动数组,拿零占位
struct node{
添加时间之类的东西看题目如何要求
}k;
queue <node> q;//这里要定义一个队列后面要使用
q.push(node{初始位置等});
bool vis[tx][ty]=true;//定义一个布尔数组后面记录有没有被走过
while(!q.empty()){
k=q.front();// 取队首
q.pop();//把队首弹出
if(到达终点条件){
cout<<k.t;
return 0;
}
for(int i=1;i<=4;i++){
dx=k.x+m[i]
dy=k.y+v[i]
if(判断越界和走过的地方and障碍物){
continue;
}
q.push(node{新位置和时间});
vis[dx][dy]=true;//标记
}
}
定义一个队列来记录走到每个地方的时间和位置。每次循环给队列里添加新的时间和位置。
4.难理解和疑点
bfs如果只看图例和模板的话可能不能理解理解里面更深层一点的东西,为避免烙下“病根”,我在这里把BFS的一些难理解和疑点给大家讲解一下
1.为什么要定义两个位置移动的数组?
这个我因该在我上篇博客(DFS)中就提到的可由于我的一些疏忽没有讲到,所以今天我在这里给大家讲一下为什么要定义两个位置移动的数组。
它就是在数组中来移动然后每次给数组的位置变动就行了,这个点非常重要尤其是在上过八年级之后很容易把它跟数学里的平面直角坐标系搞混,所以这里必须好好理解。
上图是数学中的平面直角坐标系图,如果以i表示横轴,j表示纵轴的话,它的一二三四象限分别的变化趋势为:(i+,j+),(i-,j+),(i-,j-),(i+,j-)
but我们c++中的二维数组转换成图例为
它的变化趋势图中已给,我们就可以在数组中给他实现出来。
向东:d[x][y+1] 向南:d[x+1][y] 向西:d[x][y-1] 向北:d[x-1][y]
2.为什么要用队列
学bfs的时候可能很多人会有疑问(为什么dfs只用搜索和回溯就能实现,而bfs却要用队列呢?)
这是因为dfs和bfs运用的领域不同,dfs通常是要运用排列组合问题,要把题目中给的所有可能的情况都给求出来,而bfs它通常用于求最短路径问题,队列就可以很好地帮我们实现求最短路径
(光说可能不太好理解,所以继续拿图来演示一下)
我们开始先从起点开始搜,将1压入队列里,如图:
通过1我们搜到了2,3,4然后我们把2,3,4,压进队里有以便之后更好的搜索,因为1已经搜完了我们要接着往后搜,所以我们把1,弹出队列
我们拿2搜又搜到了5,把5再压进队里,可是5前面还有3,4,只有在3,4搜完弹出队以后5才能搜这就体现了队列在bfs中的作用,本层搜完以后,才能轮到下一层搜,这样也方便我们更好地找到最短路径。
3.为什么BFS求得是最短路,为什么最后输出的是最短路?
bfs每次都是向外扩展一次,因此它们所用的时间是相同的,每次向外扩展一步所以速度也是相同的,因为速度和时间都相同,所以每段路程也是相同的,所以最后段数最少的就是最短路径
如图我们如果想到8,在第二次搜索时我们就已经在4搜到了8,接下来不管咋搜后面搜到的路径肯定会比第一次搜到的长,因此第一次搜到的路径就是最短路径。
5.做题巩固
把关于bfs的所有难点都解决了以后我们就该做题尝试,并且巩固我们的bfs
接下来请看P1443 马的遍历
这题我们看到在n×m的棋盘中有一个马,要求它到某个点的最短步数。
注意:看到最短步数,要第一个想到bfs(恩师的名言名句)
所以这题我们用bfs做就很快可以做出来,but这里有个难点就是它是在棋盘上让马走,所以这里的位置移动数组要变一变,不仅要甚至8个位置的移动,它移动的量也要变一变,其它的就直接拿bfs做就行了,上代码:
#include<bits/stdc++.h>
using namespace std;
queue<int> q,p;
int a[1001][1001],s[1001][1001],tx1,ty1,tx[9]={0,-2,-2,-1,-1,1,1,2,2},ty[9]={0,-1,1,-2,2,-2,2,-1,1},n,m,x,y;
int main()
{
cin>>n>>m>>x>>y;
q.push(x);
p.push(y);
a[x][y]=1;
while(!q.empty())
{
for(int i=0;i<=8;i++)
{
tx1=q.front()+tx[i],ty1=p.front()+ty[i];
if(tx1>0&&tx1<=n&&ty1>0&&ty1<=m)
{
if(!a[tx1][ty1])
{
a[tx1][ty1]=1;
s[tx1][ty1]=s[q.front()][p.front()]+1;
q.push(tx1);
p.push(ty1);
}
}
}
q.pop();
p.pop();
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i][j])
printf("%-5d",s[i][j]);
else
cout<<"-1 ";
}
cout<<endl;
}
}
直接就过了
下面我在推荐几道好题,非常建议下去做一下
P1332 血色先锋队要注意它可能有多个起点。
P7243 最大公约数这题做时一定要细心,注意如何写求公约数。
P1135 奇怪的电梯比较经典的bfs题,可以先尝试做一下这题。
6.如何学好BFS
这里我总结了五(四)点如何去学好bfs
1.理解模板
模板是整个算法的基础,如果连模板都理解不了,就学不了BFS。
2.查缺补漏
通过做一些基础的题来找到关于BFS的薄弱点,再学习去解决问题。
3.斩草除根
将BFS的所有疑点难点都学习透彻,保证在BFS上没有疑点。
4.做题巩固
做一些关于BFS的难题,巩固自己的bfs。
5.点赞加关注,追更不迷路。
给作者点一个赞吧,再顺手点一个关注吧。作者在这里祝愿各位的信息成绩一路高升。
求求了,点一个赞吧,阿里嘎多。