注意:本博客是一名真正的菜鸟写的,目的在于记录学习过程,以及给想要学习新算法的同学一些经验,代码写的丑了或者讲的不对了希望大家不要介意~
初识BFS
最近在某大牛的熏陶下,学习了BFS,虽说BFS作为最基础的搜索算法,学起来并不费劲,但我现在还不敢说我已经完全掌握。如果有写的不对的地方,欢迎各位大佬指正。
BFS是什么
BFS全称宽度优先搜索算法(又称广度优先搜索),是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。BFS属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。(取自百度百科)
而按我自己的理解,BFS和DFS1这对双胞胎,是最基础的搜索算法,也是最简单最粗暴最好理解的图的搜索算法,在所有图论算法中,BFS多用于求迷宫的最短路径,比较经典的模板题我会列在下面。
那么BFS的基本思路是什么呢?按照我的理解,是通过确定的某一个点,往外展开地毯式的搜索,通过储存可能存在的路径、步数、格子总数等来得最短路径、迷宫、踩格子等题目类型的解。是最基础的图算法。那么接下来上模板题。
初步理解
那么我来讲讲我学习BFS后对BFS的理解,以及讲解。(如果有讲的不好的地方请大家指出来)
这里有一张图↓
这就是一张5*5的非常经典的BFS类型迷宫图,我会用这张图讲解BFS的基本原理。
我们先找到@的位置(可以理解为起始点或是根节点)。然后从@的位置往外(上下左右)扩散,然后进行一系列的判断,将需要继续扩散的点储存,并储存步数,进入下一次循环,再从存储的点往外(上下左右)扩散,再进行一轮储存。已经踩过的点就不需要储存了,这样就可以保证你踩到这个点所经过的路径一定是最短的。而缺点就在于由于要铺开整张图,在时间复杂度上有劣势。
这里讲解的只是最基础的一种BFS,而还有不少变异的BFS,比如下面蓝桥杯比赛的卡片换位,使用BFS写就需要一定的思维能力。
模板题
1.HDU–1312 Red and Black(红与黑)
2.HDU–2612 Find a way(找到目的地)
1.Red and Black(红与黑)
这道题实际上用DFS会更优,但作为模板题来练手还是可以的。
这道题的大意是你在一个房间当中,房间的地板只有红和黑两种颜色,’.‘是红色的地板,’#‘是黑色的地板,’@'是你自己。你很喜欢红色,所以你只能走到红色的地板上,不能走到你不喜欢的黑色地板上(虽然我不知道为什么踩个地板就好像能要了你的命XD),并且你的腿比较短,一次只能走到相邻的地板上,不能斜着走。那么问题来了,你能踩到的地板有多少块?
这道题就是非常标准的图的搜索题,以下是我的AC代码:
#include "cstdio"
#include "cstring"
#include "queue"
using namespace std;
const int MAXN = 21;
bool use[MAXN][MAXN]; //判断这个点是否被'踩'过
char Map[MAXN][MAXN]; //存图
queue<int> q; //储存待扩展的点
int n,m,ans;
void BFS(){
while(!q.empty()){
int a=q.front(); //取出队列最前端的点
q.pop();
int b=q.front();
q.pop();
ans++;
if(a>0) if(Map[a-1][b]=='.'&&!use[a-1][b]){
//依次判断是否越界,是否为'.',是否被踩过
q.push(a-1); //将这个点存入队列
q.push(b);
use[a-1][b]=true; //这个点被踩过
}
if(b>0) if(Map[a][b-1]=='.'&&!use[a][b-1]){
q.push(a);
q.push(b-1);
use[a][b-1]=true;
}
if(a<m-1) if(Map[a+1][b]=='.'&&!use[a+1][b]){
q.push(a+1);
q.push(b);
use[a+1][b]=true;
}
if(b<n-1) if(Map