前言:本人是个实力很弱但立志变强的ACM小白(使用C++)。此篇BFS模板目前只有循环实现,等之后学习了队列实现或递归实现会回头补充。
BFS即广度优先搜索,在搜索一个状态下一步变化之后可能产生的状态时,先遍历出所有可能产生的新状态,然后再逐一从新状态出发搜索再下一步变化可能产生的状态。
可用树状图表达:
第一类:保存整棵搜索树
显然第一类和第二类对空间大小的要求是不一样的,能满足的题意要求也不太一样,根据实际情况选用
用一个有限列无限行的二维数组(命名为bfs)储存所有搜索结果,其中一列储存搜索层数(变化步数),其他列储存搜索结果。
辅助变量:
- ibfs - 总是表示bfs数组下一个空行的下标,其值每搜得一个结果+1;
- bfslayerlength - 总是表示当前层已搜索出的状态,其值每搜得一个结果+1,该层搜索完毕后其值即为当前层的结果数(用变量bfslayerlengthtemp维护这个值),清零之后再搜索下一层;
- isanswerfound - 用于辅助搜索到最终所需状态时跳出bfs搜索循环、判定不可能情况等;
- iibfs - 存储上一层搜索完之后ibfs的值,表示当前层的起点下标。
- bfslayerlengthtemp - 上面已经提过,用来存储搜索完一层后bfslayerlength的值,因此此值表示上一层的搜索结果数;
以上最后两个变量相结合就可以表示出上一层搜索结果的下标范围,声明它们的目的也在于此(上一层的搜索结果即搜索当前层的出发点)。以下把它们称作维护变量:
搜索过程示例:
刚刚搜索完第3层时:
bfs[][0] | bfs[][1]... | |||
---|---|---|---|---|
bfs[0][] | 1 | 状态1 | ||
bfs[1][] | 2 | 状态2 | <- iibfs - bfslayerlengthtemp | |
bfs[2][] | 2 | 状态3 | ||
bfs[3][] | 2 | 状态4 | ||
bfs[4][] | 3 | 状态5 | <- iibfs | <- ibfs - bfslayerlength |
bfs[5][] | 3 | 状态6 | ||
bfs[6][] | 3 | 状态7 | ||
bfs[7][] | 3 | 状态8 | ||
bfs[8][] | 0 | 0 | <- ibfs | |
... |
给维护变量iibfs、bfslayerlengthtemp赋值,清零bfslayerlength后,准备搜索第4层:
bfs[][0] | bfs[][1]... | |||
---|---|---|---|---|
bfs[0][] | 1 | 状态1 | ||
bfs[1][] | 2 | 状态2 | ||
bfs[2][] | 2 | 状态3 | ||
bfs[3][] | 2 | 状态4 | ||
bfs[4][] | 3 | 状态5 | <- iibfs - bfslayerlengthtemp | |
bfs[5][] | 3 | 状态6 | ||
bfs[6][] | 3 | 状态7 | ||
bfs[7][] | 3 | 状态8 | ||
bfs[8][] | 0 | 0 | <- iibfs | <- ibfs - bfslayerlength <- ibfs |
... |
搜索第4层中:
bfs[][0] | bfs[][1]... | |||
---|---|---|---|---|
bfs[0][] | 1 | 状态1 | ||
bfs[1][] | 2 | 状态2 | ||
bfs[2][] | 2 | 状态3 | ||
bfs[3][] | 2 | 状态4 | ||
bfs[4][] | 3 | 状态5 | <- iibfs - bfslayerlengthtemp | |
bfs[5][] | 3 | 状态6 | ||
bfs[6][] | 3 | 状态7 | ||
bfs[7][] | 3 | 状态8 | ||
bfs[8][] | 4 | 状态9 | <- iibfs | <- ibfs - bfslayerlength |
bfs[9][] | 4 | 状态10 | ||
bfs[10][] | 4 | 状态11 | ||
bfs[11][] | 0 | 0 | <- ibfs | |
... |
代码模板
//定义BFS所需变量
short bfs[10005][2];//不一定够用
int ibfs;
int iibfs;
int bfslayerlength;
int bfslayerlengthtemp;
bool isanswerfound;
//BFS
//初始化
for (int i = 0; i < 10005; i++)
{
bfs[i][0] = 0;
bfs[i][1] = 0;
}
bfs[0][0] = 1; bfs[0][1] = 1;//设置要搜索的起始状态
ibfs = 1;
iibfs = 1;
bfslayerlengthtemp = 1;
isanswerfound = false;
//开始搜索,此while循环每执行一次,搜索一层
while (true)
{
bfslayerlength = 0;
for (int i = iibfs - bfslayerlengthtemp; i < iibfs; i++)
{
//在这里写上搜索规则,被搜数即bfs[i][1]...,新搜索结果存储位置为,bfs[ibfs][1]...,每当发生有一个新搜索结果加入bfs数组时一定还要伴随以下这段代码
bfs[ibfs][0] = bfs[iibfs - 1][0] + 1;//这里的bfs[iibfs - 1][0]即上一层层数
if ()//括号中写上出现了所需状态时满足的条件,例如bfs[i][1]...==所需状态
{
//这里可再写上搜到所需状态之后,要在跳出循环之前执行什么,例如输出步数或层数
isanswerfound = true;
break;
}
ibfs++;
bfslayerlength++;
}
if (isanswerfound == true)//这里需再加上其他要停止bfs搜索的条件,例如层数超出规定范围、没有新增任何结果(bfslayerlength == 0)等
break;
bfslayerlengthtemp = bfslayerlength;
iibfs = ibfs;
}
if (isanswerfound == false)
{
//这里写上不可能搜索到所需状态时要做什么
}
第二类:“边搜边扔”,只保存当前层和上一层搜索结果,但在搜索树分枝数量庞大时能节省不少空间
用两个无限一维数组bfs、bfstemp分别储存当前层和上一层搜索结果(或无限行有限列二维数组,储存一种状态需要多少个数据就用多少列),第一行(即下标为0的位置)储存该组搜索结果所在层数,其他行储存搜索结果。
先将起始状态放入bfstemp,搜索bfstemp中的状态,搜索结果放入bfs,然后再将bfs拷贝至bfstemp并清空bfs,再次从bfstemp里的数据出发搜索下一层...以此反复循环。
bfstemp | bfs | bfstemp | bfs | bfstemp | bfs | |||
[0] | 2 | 3 | 3 | 4 | 3 | 4 | ||
[1] | 状态2 | 状态4 | 状态4 | 0 | 状态4 | 状态8 | ||
[2] | 状态3 | 状态5 | -> | 状态5 | 0 | -> | 状态5 | 状态9 |
[3] | 0 | 状态6 | 状态6 | 0 | 状态6 | 状态10 | ||
[4] | 0 | 状态7 | 状态7 | 0 | 状态7 | 状态11 | ||
[5] | 0 | 0 | 0 | 0 | 0 | ... |
辅助变量:
- ibfs - 总是表示数组bfs的下一空位下标,作为bfs数组终点依据;
- ibfstemp - 总是表示数组bfstemp的下一空位下标,作为bfstemp数组终点依据;
- isanswerfound - 作用同第一类;
与第一类相比,第二类只要这么多辅助变量就够了。
代码模板
short bfstemp[10005];
int ibfs;
int ibfstemp;
bool isanswerfound;
//BFS
//初始化
for (int i = 0; i < 10005; i++)
{
bfs[i] = 0;
bfstemp[i] = 0;
}
bfstemp[0] = 1; bfstemp[1] = 1;//设置要搜索的起始状态
ibfs = 1;
ibfstemp = 1;
isanswerfound = false;
//开始搜索,此while循环每执行一次,搜索一层
while (true)
{
ibfs = 1;
for (int i = 1; i < ibfstemp; i++)
{
//这里写上搜索规则,被搜数为bfstemp[i],新结果存储位置为bfs[ibfs],每发生有新搜索结果写入要伴随以下这段代码
if ()//括号中写上出现了所需状态时满足的条件,例如bfs[i]==所需状态
{
//这里可再写上搜到所需状态之后,要在跳出循环之前执行什么,例如输出步数或层数
isanswerfound = true;
break;
}
ibfs++;
}
if (isanswerfound == true)//这里需再加上其他要停止bfs搜索的条件,例如层数超出规定范围、没有新增任何结果(ibfs == 1)等
break;
for (int i = 0; i < ibfs; i++)//将bfs拷贝到bfstemp,清零bfs
{
bfstemp[i] = bfs[i];
bfs[i] = 0;
}
bfs[0] = bfstemp[0] + 1;//赋值层数
}
if (isanswerfound == false)
{
//这里写上不可能搜索到所需状态时要做什么
}
修改日志
将代码模板从“先搜索完整层再逐一检查这一层有无出现所需状态”改为“每搜索出一个新状态立即检查其是否为所需状态”(2019.2.5 15:35提交)