UVA_10422
一开始本来打算用BFS+Hash判重去做,但是空格有些不好处理,后来发现由于限定了解的层数最多为10,那么直接用DFS也不是很困难的事情。
如果直接DFS,节点数是8^10,还是有些多了,但我们可以进行适当的剪枝,我想的剪枝策略有下面两个:
①当n=9时,如果空格与中心的曼哈顿距离不等于3,那么就不用再往下搜了,肯定无解。如果曼哈顿距离为3,那么第10步空格一定会走到中央。
②对于当前状态,假如已经移动了n步,我们统计所得的棋盘主对角线及其上部白马的个数为p,如果p-2>10-n,也不用继续往下搜了,肯定无解。因为上半部分白马的容量最多为2,p-2为多余的白马,剩余可走步数至少应该让这些多余的白马全走出来。
#include<stdio.h>
#include<string.h>
#include<math.h>
int target[5][5]={{1,1,1,1,1},{0,1,1,1,1},
{0,0,-1,1,1},{0,0,0,0,1},{0,0,0,0,0}};
int a[5][5],ans;
char b[10];
int dx[]={-2,-2,-1,1,2,2,1,-1};
int dy[]={-1,1,2,2,1,-1,-2,-2};
void dfs(int x,int y,int n)
{
int i,j,k,p,num,newx,newy;
if(memcmp(a,target,sizeof(target))==0)
{
if(n<ans)
ans=n;
return;
}
if(n==9)
{
p=abs(x-2)+abs(y-2);
if(p!=3)
return;
k=a[x][y];
a[x][y]=a[2][2];
a[2][2]=k;
if(memcmp(a,target,sizeof(target))==0&&10<ans)
ans=10;
a[2][2]=a[x][y];
a[x][y]=k;
return;
}
p=0;
for(i=0;i<5;i++)
for(j=i;j<5;j++)
if(a[i][j]==0)
p++;
if(p-2>10-n)
return;
for(i=0;i<8;i++)
{
newx=x+dx[i];
newy=y+dy[i];
if(newx>=0&&newx<5&&newy>=0&&newy<5)
{
k=a[x][y];
a[x][y]=a[newx][newy];
a[newx][newy]=k;
dfs(newx,newy,n+1);
a[newx][newy]=a[x][y];
a[x][y]=k;
}
}
}
int main()
{
int i,j,k,x,y,N,t;
gets(b);
sscanf(b,"%d",&N);
while(N--)
{
for(i=0;i<5;i++)
{
gets(b);
for(j=0;j<5;j++)
{
if(b[j]==' ')
{
a[i][j]=-1;
x=i;
y=j;
}
else
a[i][j]=b[j]-'0';
}
}
ans=11;
dfs(x,y,0);
if(ans<11)
printf("Solvable in %d move(s).\n",ans);
else
printf("Unsolvable in less than 11 move(s).\n");
}
return 0;
}