学习笔记 #2:启发式搜索
分为两种:A* 和 IDA*
A* :BFS,极其类似Dijkstra堆优化
IDA* :迭代加深DFS,比A*好写,适用于限制深度的题
需要估价函数 \(h(x)\),作为判断是否先行搜索的基准。
A*
一个好的估价函数的选择会影响程序的最终效率。任意时刻下估价函数的值小于当前结点到目标结点的实际距离,否则可能使算法失效而无法得出“最优路径”。好的估价函数会在不断搜索中逐渐变得“精确”(搜索到后面时 \(h[x]\) 的影响逐渐变大,最终占据主导,若该函数值越接近真实值,计算出的价值也越接近实际),越精确程序效率越高。
在bfs的过程中,计算每个点的 \(g[x]+h[x]\) 并加入优先队列中,每次选取价值最高的点扩充路径并记录父节点(便于记录路径)即可
例题:P1379 八数码难题
#include<bits/stdc++.h>
using namespace std;
int x, y;
int goal[4][4] = {
{-1, -1, -1, -1},
{-1, 1, 2, 3},
{-1, 8, 0, 4},
{-1, 7, 6, 5}
};
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
struct sit {
int f[4][4];
bool operator < (const sit &nl) const {
for(int i = 1; i <= 3; i++) {
for(int j = 1; j <= 3; j++) {
if(f[i][j] != nl.f[i][j]) return f[i][j] < nl.f[i][j];
}
}
return 0;
}
};
int g(sit k) {
int res = 0;
for(int i = 1; i <= 3; i++) {
for(int j = 1; j <= 3; j++) {
if(k.f[i][j] != goal[i][j]) res++;
}
}
return res;
}
struct node {
int d;
sit s;
bool operator < (const node &nl) const {
return d + g(s) > nl.d + g(nl.s);
}
} a;
priority_queue <node> Q;
set <sit> s;
void A_Star() {
Q.push(a);
while(!Q.empty()) {
node tp = Q.top(); Q.pop();
if(!g(tp.s)) {
cout << tp.d;
exit(0);
}
for(int i = 1; i <= 3; i++) for(int j = 1; j <= 3; j++) if(tp.s.f[i][j] == 0) x = i, y = j;
for(int i = 0; i < 4; i++) {
int xt = x + dx[i], yt = y + dy[i];
if(xt < 1 or xt > 3 or yt < 1 or yt > 3) continue;
swap(tp.s.f[x][y], tp.s.f[xt][yt]);
if(!s.count(tp.s)) {
s.insert(tp.s);
Q.push((node){tp.d + 1, tp.s});
}
swap(tp.s.f[x][y], tp.s.f[xt][yt]);
}
}
}
int main() {
string str; cin >> str;
for(int i = 1; i <= 3; i++) for(int j = 1; j <= 3; j++) a.s.f[i][j] = str[(i - 1) * 3 + j - 1] - '0';
a.d = 0;
A_Star();
return 0;
}
IDA*
每次搜索时限制搜索层数,加快效率,加记忆化可以使效率提高,不过会麻烦。
适用于层数较少的搜索。
#include<bits/stdc++.h>
using namespace std;
int TT;
int x, y, st_x, st_y;
int a[6][6];
int ans, success;
int goal[6][6] = {
{-1, -1, -1, -1, -1},
{-1, 1, 1, 1, 1, 1},
{-1, 0, 1, 1, 1, 1},
{-1, 0, 0, 2, 1, 1},
{-1, 0, 0, 0, 0, 1},
{-1, 0, 0, 0, 0, 0},
};
int dx[8] = {-2, -2, -1, -1, 1, 1, 2, 2}, dy[8] = {-1, 1, -2, 2, -2, 2, -1, 1};
int g() {
int res = 0;
for(int i = 1; i <= 5; i++) {
for(int j = 1; j <= 5; j++) {
if(a[i][j] != goal[i][j]) res++;
}
}
return res;
}
void IDA_Star(int dep, int x, int y, int maxdep) {
if(dep == maxdep) {
if(!g()) success = 1;
return;
}
for(int i = 0; i < 8; i++) {
int xt = x + dx[i], yt = y + dy[i];
if(xt < 1 or xt > 5 or yt < 1 or yt > 5) continue;
swap(a[x][y], a[xt][yt]);
if(dep + g() <= maxdep) IDA_Star(dep + 1, xt, yt, maxdep);
swap(a[x][y], a[xt][yt]);
}
}
int main() {
scanf("%d", &TT);
while(TT--) {
for(int i = 1; i <= 5; i++) for(int j = 1; j <= 5; j++) {
char c; scanf(" %c", &c); if(c == '*') a[i][j] = 2, st_x = i, st_y = j; else a[i][j] = c - '0';
}
ans = -1; success = 0;
for(int i = 1; i <= 15; i++) {
IDA_Star(0, st_x, st_y, i);
if(success) {
ans = i; break;
}
}
printf("%d\n", ans);
}
return 0;
}
代码很容易理解,写出来有点令人断片,多打打题就好了,难点就在于估价函数。