题目描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入格式
输入初始状态,一行九个数字,空格用0表示
输出格式
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
除了搜索还真的没有方法下手了。我们可以搜索空格如何走,那么状态就为:
当前的棋盘,空格的位置和走过的步数。棋盘可以用全局变量来存。每次往上下左右四个方向搜即可。
考虑剪枝
1.上下界剪枝:
上界:不好求
下界:最好的情况就是——当前情况有多少个位子与目标状态不同,即
[sum_{i=1}^{3}sum_{j=1}^{3}[g[i][j]≠des[i][j]]
]
虽然下界对剪枝没什么用
2.搜索顺序优化:由于一个地方可以重复走所以顺序随意。
3.排除等效冗余:往前走一步然后往回走一步没有意义,所以我们应该避免往上一次的反方向走。
4.最优化剪枝:设当前的答案为ans,那么当深度>ans时就没必要搜了。
5.记忆化:日常没什么好记的
并没有剪掉多少诶......考虑到答案很小(观察法),我们可以用迭代加深来优化DFS。
从1开始枚举深度,由迭代加深性质可知当第一次搜索到答案时必定是最优解,所以我们没必要做最优化剪枝了。
显然加了迭代加深之后固然能够起作用,但是我们又扔掉了一个好的剪枝——最优化剪枝,实际上效果不明显。并且剪枝已经剪不动了,当搜索到答案的深度时,我们必然会面对一个庞大的搜索树。
由于IDFS搜到的第一个答案就是最优解,所以搜到后可以直接跳出搜索。我们只需要让程序更快地搜到最优解即可。这时就加个启发式IDA * 吧~
设计估价函数。结合上面提到的下界,我们可以设计这样一个函数:
[f(g[][])=sum_{i=1}^{3}sum_{j=1}^{3}[g[i][j]≠des[i][j]]
]
当当前深度加上预估值>当前的最大深度时就不需要往下搜了。
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 4
using namespace std;
const int des[4][4]={{},{0,1,2,3},{0,8,0,4},{0,7,6,5}};
const int dir[4][2]={{-1,0},{0,-1},{0,1},{1,0}};
int g[maxn][maxn],ans;
inline int Abs(const int &x){ return x<0?-x:x; }
inline int evaluate(){
int cnt=0;
for(register int i=1;i<=3;i++) for(register int j=1;j<=3;j++) if(g[i][j]!=des[i][j]) cnt++;
return cnt;
}
inline bool check(const int &x,const int &y){ return x<1||3<x||y<1||3<y; }
void IDAstar(int x,int y,int dep,int pre_dir,const int &maxdep){
if(dep==maxdep){ if(!evaluate()) ans=true; return; }
for(register int i=0;i<4;i++) if(i+pre_dir!=3){
int tx=x+dir[i][0],ty=y+dir[i][1];
if(!check(tx,ty)){
swap(g[x][y],g[tx][ty]);
if(dep+evaluate()<=maxdep) IDAstar(tx,ty,dep+1,i,maxdep);
swap(g[x][y],g[tx][ty]);
if(ans) return;
}
}
}
int main(){
int sx,sy;
for(register int i=1;i<=3;i++){
for(register int j=1;j<=3;j++){
scanf("%1d",&g[i][j]);
if(!g[i][j]) sx=i,sy=j;
}
}
if(!evaluate()){ puts("0"); goto Lzs; }
for(register int maxdep=1;;maxdep++){
IDAstar(sx,sy,0,-1,maxdep);
if(ans){ printf("%d
",maxdep); break; }
}
Lzs:;
return 0;
}