前言
BFS 全称是 \(Breadth First Search\),中文名是宽度优先搜索,也叫广度优先搜索。
是图上最基础、最重要的搜索算法之一。
所谓宽度优先。就是每次都尝试访问同一层的节点。 如果同一层都访问完了,再访问下一层。
这样做的结果是,BFS 算法找到的路径是从起点开始的 最短 合法路径。换言之,这条路所包含的边数最小。
在 BFS 结束时,每个节点都是通过从起点到该点的最短路径访问的。
算法过程可以看做是图上火苗传播的过程:最开始只有起点着火了,在每一时刻,有火的节点都向它相邻的所有节点传播火苗。
当然也可以应用于其他的一些结构中进行搜索
框架
void BFS()
{
memset(vis,0,sizeof(vis));
queue<数据类型> q;
q.push(初始值);
vis[初始值]=1;
while(!q.empty())
{
int k=q.front();
q.pop();
//....操作,处理
if(符合条件)
{
q.push(当前节点);
vis[当前节点]=1;
}
}
}
例题讲解
P2730 [USACO3.2]魔板 Magic Squares
思路
根据题目要求先用函数处理好每一个状态,运用\(set\)判重,因为每一层循环的操作都会是所有的操作数\(+1\),每一次弹出时判断一下是不是最终状态即可
代码
#include
#include
#include
#include
#include
#include
LOJ10028.Knight Moves
思路
存一下每一次走的步数,然后直接广搜就完事了,遇到最终情况就返回后输出,注意判重和打标记,因为是多次询问,别忘了清空堆里面存的值和 \(vis\) 的值
代码
#include
#include
#include
#include
#include
P4289 [HAOI2008]移动玩具
思路
对于已经匹配好的点,直接赋值为 \(0\) 不用再去管他,对于没有匹配的点,就继续是原先的位置,再找一个数组存一下没有匹配的位置,以及与所有可以进行匹配的点之间的曼哈顿距离,然后用常规的深搜(乱入)比较一下最小值即可
代码
#include
#include
#include
#include
#include
#include
#include
P6909 [ICPC2015 WF]Keyboarding
坑点提醒
1、最后需要你手动添加一个\(“*”\)
2、注意在其实位置按键的情况
思路
首先我们可以把所有相关涉及到的字符都映射为数字,方便处理,包括\((A…Z),(1…9),("*""-")\)这些东东,处理的时候别映射 \(0\) 就行
当我们输入的时候直接映射到一个二维数组 \(a\) 来装载键盘的每个键
将目标串映射到一个一维数组 \(b\) 中来需要按得每一个键,最后别忘了手动添加一位来存 \("*"\)
那么如何选择位置呢,如果在搜索的过程中暴力美剧会非常耗时间,那么这个时候就可以预处理每一个键第一个到达的位置即可
为了方便我们要设一个结构体类型的三维数组
结构体的变量分别为 到达的位置的横坐标 \(x\) ,到达位置的纵坐标 \(y\) ,到达这个位置的时候要匹配目标串的哪一位 \(step\) ,以及到这个点已经走了几步 \(dis\)
三维数组 \(f[k][i][j]\) 表示从 \((i,j)\)向 \(k\) 方向走到达的第一个位置的信息(即结构体中的内容)
那么预处理之后就开始广搜了
在开始广搜之前,因为我们是从第一位开始的,那么我们最好先预处理一下在第一位一共可以连续按几次,然后再把结构体重所表示的数据存进去,在代码中有就不详细说了
那么现在开始广搜
考虑两种情况,我们要把选择和匹配分开找
一、
那么先是匹配,要看一下堆顶的这个位置是否和当前要匹配的位置相同,如果是相同的时候,那就在进行判断一下这个是不是最终状态,如果是,那直接把答案赋值为当前的步数+1就是最有的答案
那如果不是最后的一个情况呢
可以设一个 \(vis\) 数组,用来存当前堆顶的点接下来该匹配哪一位了,然后把相应的结构体中的数据都放进去继续查找
二、
最后再开始选择
首先枚举该点四个位置的到达点,当然因为我在预处理位置的时候有一个位置得移动忘加了,所以还要加上,一次移动
首先判断一下是否越界
然后再判断一下当前要选择的地方要比原坐标的要处理的位置要更大,那么就不需要更新 \(vis\) 数组。
否则的话,就更新当前的 \(vis\) 数组为现在要处理的位置(不用+1,因为是选择,即跳过当前的匹配直接跳进),然后把当前要选择的位置的数据放进去
最后直接输出即可
代码
#include
#include
#include
#include
#include
#include
#include
#include
P3456 [POI2007]GRZ-Ridges and Valleys
思路
运用广搜板子去做即可,每一次搜索的时候都把找到的高度相同的点标记一下,避免进入死循环,一开始进入搜索的时候先把山峰山谷的值设为 \(1\),然后根据后续的判断,把不匹配的都设为 \(0\) ,最后加上即可
代码
//#include
//#include
//#include
//#include
//#include
//#include
结语
当做一个题目的时候,尽量先有爆搜的思路稳住分数,把暴力分拿满之后再去思考正解,稳重求进。