假设现在有一个任务,这个任务要通过深度优先搜索(DFS)完成,并且搜索深度可能很深,比如 20 20 20 层。但是答案一般在 5 5 5 层。
怎么设计搜索可以尽快完成任务?
DFS
深度优先搜索(DFS),通过递归实现。通过从一个状态扩散出很多个状态,一直搜索直到搜索结束条件满足。通常可以解决很多问题。深度优先搜索广泛应用于各种问题。但是深度优先搜索的效率是个问题。
对于上面情景的问题,搜索可能从一个分支搜索了很久,然后发现在另一个分支搜了三次就搜到了。
IDDFS
这个时候可以用迭代加深搜索进行 DFS。
迭代加深搜索(IDDFS)本质是 DFS,是通过枚举搜索最大层数,如果讲最大层数设置为 x x x 可以有答案是就可以输出答案,否则,将 x x x 加上 1 1 1 后继续搜索。这样可以有效规避答案层数很浅的情况,但是会稍微耗时一点。
如果答案通常在很浅的地方,IDDFS 会发挥很大的作用。
伪代码实现
IDDFS(初始状态,最大层数, 层数):
如果 层数 == 最大层数:
返回 -1
从 初始状态 扩散出状态为 下一个状态:
下一状态答案 为 IDDFS(下一个状态, 最大层数, 层数 + 1)
如果 下一状态答案 不为 -1:
返回 下一状态答案
返回 -1
获取答案:
让 x 从 1 到 100 循环:
当前答案 = IDDFS(初始状态, x, 0)
如果 当前答案 不为 -1:
返回 当前答案
IDA*
IDA* 是一个在 IDDFS 中加上了估价函数的搜索方式。总体原理和 IDDFS 相似,但是在实现是会加上一个估价函数以剪枝。估价函数是用来判断这个状态可不可能到达终点的。
伪代码实现
IDDFS(初始状态,最大层数, 层数):
如果 层数 == 最大层数:
返回 -1
如果 估价(初始状态, 层数) 为假:
返回 -1
从 初始状态 扩散出状态为 下一个状态:
下一状态答案 为 IDDFS(下一个状态, 最大层数, 层数 + 1)
如果 下一状态答案 不为 -1:
返回 下一状态答案
返回 -1
获取答案:
让 x 从 1 到 100 循环:
当前答案 = IDDFS(初始状态, x, 0)
如果 当前答案 不为 -1:
返回 当前答案
例题
洛谷P2324 骑士精神
此题使用 IDA* 实现。先枚举最大步数,如果当前步数可以得到答案,输出这个步数。
检查答案函数内部,就是 IDA*。估价函数的设计,每 k k k 次移动,最多让答案和当前状态减少 k − 1 k - 1 k−1 处。如果我们还可以移动的次数在答案状态和当前状态不同处的个数外,就不可以实现,返回 0 0 0。这样可以让计算量大大的减少。
AC Code:
#include <bits/stdc++.h>
using namespace std;
int T;
char s[10][10];
char t[10][10] = {
"",
" 11111",
" 01111",
" 00*11",
" 00001",
" 00000"
};
int cnt() {
int ret = 0;
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
if (t[i][j] != s[i][j])
ret++;
}
}
return ret;
}
int dx[10] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dy[10] = {-1, 1, -2, 2, -2, 2, -1, 1};
int x, y;
int c;
bool dfs(int dep, int mdep, int x, int y) {
c = cnt();
if (dep == mdep) {
if (!c)
return 1;
return 0;
}
if (c + dep - 1 > mdep)
return 0;
for (int i = 0; i < 8; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx >= 1 && nx <= 5 && ny >= 1 && ny <= 5) {
swap(s[x][y], s[nx][ny]);
if (dfs(dep + 1, mdep, nx, ny)) {
return 1;
}
swap(s[x][y], s[nx][ny]);
}
}
return 0;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> T;
while (T--) {
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
cin >> s[i][j];
if (s[i][j] == '*') {
x = i, y = j;
}
}
}
bool flag = 0;
for (int i = 0; i <= 15; i++) {
if (dfs(0, i, x, y)) {
cout << i << '\n';
flag = 1;
break;
}
}
if (!flag) {
cout << "-1\n";
}
}
return 0;
}