棋盘太大,状态数太多,无法像八数码那样用哈希+BFS去做,但是用DFS也会T掉,这个时候可以考虑IDA*,即 迭代加深搜索 + A*剪枝。
这题的h(n)就是当前与目标棋盘不同的棋子数量。在一颗含有答案的子树中,我们至多需要再走h(n)步,就可以找到答案
因此,当当前步数+h(n) > 最大可走步数时,就可以剪枝
于是现在要求最大可走步数,这里就体现了迭代加深,每次加大最大可走步数(直到15步),在强制认为答案等于迭代数的思想下搜索,如果搜索到目标点时直接输出迭代数,搜不到迭代数加1。这样做的好处是,不需要再开一个dis数组记录距离。
for(fd=0; fd<=15; fd++) {
dfs(x,y,0);
if(flg) {
printf("%d\n",fd);
break;
}
}
并且迭代加深有时是比DFS快的,因为对于一些问题,从根出发的每一棵搜索子树都很深,或者说搜索树在某个方向上是无限的,举个栗子,设存在子树1,子树2,每一棵子树深度十分十分大,然后答案存在于子树2的第1层,如果用DFS可能都会爆栈,但是用迭代加深几次就搜到了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define debug(x) cerr<<#x<<"="<<x<<endl;
int goal[5][5] = {
1,1,1,1,1,
0,1,1,1,1,
0,0,2,1,1,
0,0,0,0,1,
0,0,0,0,0,
};
int a[6][6];
int n, t, ans, fd, hd, x, y;
bool flg;
inline int h() {
int num = 0;
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
if(a[i][j] != goal[i][j])
num++;
return num;
}
inline bool check() {
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
if(a[i][j] != goal[i][j])
return false;
return true;
}
int dx[8] = {1,1,2,2,-1,-1,-2,-2};
int dy[8] = {-2,2,-1,1,2,-2,1,-1};
void dfs(int x, int y, int d) {
if(d == fd) {
if(check())
flg = true;
return;
}
if(flg) return;
for(int i=0; i<8; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if(nx>=0 && nx<5 && ny>=0 && ny<5) {
swap(a[nx][ny],a[x][y]);
int hh = h();
if(d+hh <= fd)
dfs(nx, ny, d+1);
swap(a[x][y],a[nx][ny]);
}
}
}
int main() {
cin >> t;
while(t--) {
flg = false;
memset(a,0,sizeof(a));
for(int i=0; i<5; i++) {
char temp[6];
cin >> temp;
for(int j=0; j<5; j++) {
if(temp[j] == '*') {
a[i][j] = 2;
x = i, y = j;
continue;
}
a[i][j] = temp[j] - '0';
}
}
for(fd=0; fd<=15; fd++) {
dfs(x,y,0);
if(flg) {
printf("%d\n",fd);
break;
}
}
if(!flg)
printf("-1\n");
}
return 0;
}