这是一种很奇特的算法。。
当然理解了以后就不那么奇特了。。
直接看题:
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
样例输入:
283104765
友情样例输入图:
样例输出:
4
爱心样例解释:
把8下移一位,再将2右移一位,将1上移一位,最后将8左移一位。
提示:搜索
思考:1.可以用BFS实现吗
2.可以直接DFS吗
显然不可以。。
为什么呢?
先思考DFS:
DFS有一些很重要的东西。第一是边界,其次是一些剪枝之类的。很显然,这道题你不知道移动几步才能到达目标状态,也就是你无法定出一个边界,让你的程序停下来。
再思考BFS:
每一层都有四种选择,对应四种状态。不断扩展,直到有一种与目标状态相等,就结束,看似很完美的实现了。但我们深入思考一下,
你如何储存每一种状态,明显无法储存。
针对这种没有边界的搜索题,就出现了A*和IDA*(假定一个边界,进行搜索,若达到要求,就停止,否则将边界扩展一层,再次搜索),而IDA*是A*的加强版,效率更高。其实和DFS相比就多了一句话,下面给出代码:
#include<bits/stdc++.h>
#pragma G++ optimize(2)
usingnamespacestd;
#define fz(i,j,k) b[i][j]=int(s[k]-'0')
strings;
int k,l,r;
int a[4][4]={0,0,0,0,0,1,2,3,0,8,0,4,0,7,6,5}//目标状态,b[4][4]={};
bool bd()//判断与目标状态是否相等
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
{
if(a[i][j]!=b[i][j])
return0;
}
return1;
}
bool find(int x,int y,int s,int p)//x,y是0所在的坐标,s为移动步数,p是一个剪枝思想,用来记录上一层移动的方向,如果这一层往回走,显然不是最优的。
{
if(bd())
{
printf("%d",s);
return1;
}
if(s==k)return0;
if(x>1&&p!=1)
{
b[x][y]=b[x-1][y];
b[x-1][y]=0;
if(find(x-1,y,s+1,2))
{
b[x-1][y]=b[x][y];
b[x][y]=0;
return1;
}
b[x-1][y]=b[x][y];
b[x][y]=0;
}
if(x<3&&p!=2)
{
b[x][y]=b[x+1][y];
b[x+1][y]=0;
if(find(x+1,y,s+1,1))
{
b[x+1][y]=b[x][y];
b[x][y]=0;
return1;
}
b[x+1][y]=b[x][y];
b[x][y]=0;
}
if(y>1&&p!=3)
{
b[x][y]=b[x][y-1];
b[x][y-1]=0;
if(find(x,y-1,s+1,4))
{
b[x][y-1]=b[x][y];
b[x][y]=0;
return1;
}
b[x][y-1]=b[x][y];
b[x][y]=0;
}
if(y<3&&p!=4)
{
b[x][y]=b[x][y+1];
b[x][y+1]=0;
if(find(x,y+1,s+1,3))
{
b[x][y+1]=b[x][y];
b[x][y]=0;
return1;
}
b[x][y+1]=b[x][y];
b[x][y]=0;
}
return0;
}
int main()
{
cin>>s;
int p=0;
for(int i=0;i<s.size();i++)
{
k=i+1;int t;
if(k%3==0)t=3,k--;
else t=k%3;
fz(k/3+1,t,i);
if(b[k/3+1][t]!=a[k/3+1][t]) p++;//
if(b[k/3+1][t]==0) l=k/3+1,r=t;
}
for(k=p/2;!find(l,r,0,0);k++);//与DFS有区别的地方,k即为假定的边界,p/2是一个略微的估价,因为一次交换最多修改两个不等的方块,p为统计出来与目标不等的方块数,那么至少p/2次到达目标。
return0;
}
感悟:IDA*比A*高效,且写起来比较简单,还容易理解,不知道要A*干什么的。。