八数码是一道极为经典的宽搜题目,在此题目如下:
3×3九宫棋盘,放置数码为1-8的8个棋牌,剩下一个空格,只能通过棋牌向空格的移动来改变棋盘的布局。要求:根据给定初始布局,问:至少移动几次才能从初始布局到达目标布局。
目标布局如下图:
Input Description
3行,每行3个0-8的不重复整数,其中0表示空格所在的位置,数字间用空格隔开,表示初始布局,数据保证该初始布局一定能移到目标布局。
Output Description
一个整数,表示最少移动到目标布局的次数。
Sample
INPUT
0 7 6 8 4 3 5 2 1
OUTPUT
4
对于每种情况,可以看作9个数字的不同排列,也就是说会有9!=362880种情况,而每种情况我用一个字符串进行记录,也方便调用字符串函数减少工作量。为了避免搜索重复情况,将建立一个HASH表。每种排列的康拓展开是唯一,故可以此作为对应关系。
康拓展开,即得到该排列在全部排列中的大小排名,具体公式是:X=a[n]*(n-1)!+……+a[1]*0! 其中a[n]为其后比该位数字小的数的个数,即逆序数。
具体代码实现如下:
#include<stdio.h>
#include<string.h>
char aim[10]="876543210";
char step[400000][10];
int hash[400000];
int cantor(char x[],int n)
{
int fac[]={1,1,2,6,24,120,720,5040,40320};
int i,j,num=0,count;
for(i=0;i<n;i++)
{
count=0;
for(j=i+1;j<n;j++)
if(x[j]<x[i]) count++;
num+=count*fac[n-1-i];
}
return num+1;
}
int makestep(int p,int q,int n)
{
int i,j=q+1,t,num;
char newstep[10];
for(i=p;i<=q;i++)
{
if(strcmp(step[i],aim)==0)
{
printf("%d",n);
return 0;
}
t=0;
while(step[i][t]!='0') t++;
if(t>2)
{
strcpy(newstep,step[i]);
newstep[t]=step[i][t-3];
newstep[t-3]=step[i][t];
num=cantor(newstep,9);
if(hash[num])
{
strcpy(step[j],newstep);
hash[num]=0;
j++;
}
}
if(t<6)
{
strcpy(newstep,step[i]);
newstep[t]=step[i][t+3];
newstep[t+3]=step[i][t];
num=cantor(newstep,9);
if(hash[num])
{
strcpy(step[j],newstep);
hash[num]=0;
j++;
}
}
if(t%3!=0)
{
strcpy(newstep,step[i]);
newstep[t]=step[i][t-1];
newstep[t-1]=step[i][t];
num=cantor(newstep,9);
if(hash[num])
{
strcpy(step[j],newstep);
hash[num]=0;
j++;
}
}
if(t%3!=2)
{
strcpy(newstep,step[i]);
newstep[t]=step[i][t+1];
newstep[t+1]=step[i][t];
num=cantor(newstep,9);
if(hash[num])
{
strcpy(step[j],newstep);
hash[num]=0;
j++;
}
}
}
makestep(q+1,j-1,n+1);
return 0;
}
int main()
{
int i,trans;
for(i=0;i<9;i++)
{
scanf("%d",&trans);
step[0][i]=trans+'0';
}
step[0][9]='\0';
for(i=0;i<400000;i++) hash[i]=1;
hash[cantor(step[0],9)]=0;
makestep(0,0,0);
return 0;
}