一、BFS 模板
如下所示
set<Node> visited;
bool check(Node son);
int bfs(Node start) { queue<Node> q; q.push(start); visited.insert(start); while (!q.empty()) { Node front = q.front(); q.pop(); for (son : q.neigbour) { if (check(son)) { q.push(son); visited.insert(son); if (ac(son)) { return son.info(); } } } } return -1; }
int main() { int ans = bfs(); return 0; }
|
其中有几部分需要一一强调,第一个是 check() 函数用于进行减枝,只有经过 check 的子节点会被加入队列。
check 的基础内容就是有没有被 visited 过,其写法如下
visited.count(son) == 0 visited.find(son) == visited.end()
|
这两种写法都表明 visited 中没有 son 。
另外就是有的时候 BFS 的遍历会伴随着诸多与 Node 对应的数据的记录,比如说每个 Node 对应的 level 这样的信息,建议使用一个(或者多个)全局的 map 去这样存储
而一旦存在这样的 map,那么就可以省略 visited 了,因为没有必要了。判断是否存在,可以用
levels.count(son) == 0; levels.find(son) == visited.end();
|
同时当我们使用 BFS 的时候,我们一般是希望获得最优解(对应最小的 level),所以我们一般就遍历到合适的 Node ,就需要返回了,所以我们应当将 BFS 写成一个函数,然后方便 return 跑路。但是这就要求 BFS 需要的变量尽量是全局变量,方便访问。
二、双向 BFS
其思想是,如果 BFS 的起点和终点是可以确定的,那么就分别从起点和终点进行 BFS,最终的判断条件是两棵树是否相交(本质是两个 visited 相交),这样做的好处是可以去掉大量的无用的遍历,同时保有了 BFS 的特性
单向搜索:

双向搜索:

其在实现上基本上就是普通 BFS 复制一遍,唯一需要注意的就是利用 visit 相交的判断,板子题如下
[NOIP2002 提高组] 字串变换
题目描述
已知有两个字串 A,B 及一组字串变换的规则(至多 6 个规则),形如:
- A1→B1。
- A2→B2。
规则的含义为:在 A 中的子串 A1 可以变换为 $ B_1,A_2$ 可以变换为 B2⋯。
例如:A=abcd,B=xyz,
变换规则为:
- abc→xu,ud→y,y→yz。
则此时,A 可以经过一系列的变换变为 B,其变换的过程为:
- abcd→xud→xy→xyz。
共进行了 3 次变换,使得 A 变换为 B。
输入格式
第一行有两个字符串 A,B。
接下来若干行,每行有两个字符串 Ai,Bi,表示一条变换规则。
输出格式
若在 10 步(包含 10 步)以内能将 A 变换为 B,则输出最少的变换步数;否则输出 NO ANSWER!。
样例 #1
样例输入 #1
样例输出 #1
提示
对于 100% 数据,保证所有字符串长度的上限为 20。
【题目来源】
NOIP 2002 提高组第二题
此时的题解如下
#include <bits/stdc++.h>
using namespace std;
string a[10], b[10];
int main() { string origin, target; cin >> origin >> target; int n = 0; while (cin >> a[n] >> b[n]) { n++; }
queue<string> up, down; map<string, int> upStep, downStep; up.push(origin); upStep[origin] = 0; down.push(target); downStep[target] = 0; for (int step = 1; step <= 5; step++) { while (upStep[up.front()] == step - 1) { string front = up.front(); up.pop();
for (int i = 0; i < n; i++) { for (int pos = front.find(a[i]); pos != -1; pos = front.find(a[i], pos + 1)) { string tmp = front; tmp.replace(pos, a[i].length(), b[i]); if (upStep.find(tmp) == upStep.end()) { up.push(tmp); upStep[tmp] = step; } if (downStep.find(tmp) != downStep.end()) { cout << step * 2 - 1; return 0; } } } }
while (downStep[down.front()] == step - 1) { string front = down.front(); down.pop();
for (int i = 0; i < n; i++) { for (int pos = front.find(b[i]); pos != -1; pos = front.find(b[i], pos + 1)) { string tmp = front; tmp.replace(pos, b[i].length(), a[i]); if (downStep.find(tmp) == downStep.end()) { down.push(tmp); downStep[tmp] = step; } if (upStep.find(tmp) != upStep.end()) { cout << step * 2; return 0; } } } } }
cout << "NO ANSWER!";
return 0; }
|
需要注意的是,这里用了固定迭代次数来进一步减枝,但是这并不是 BFS 的必要特征。