八数码:
这里面需要了解一个康托展开式:a,b,c,d,e n=4!*a[0]+3!*a[1]+2!*a[2]+1!*a[3]+0!*a[4],a[ ]数组里面的值为从这个数开始,往后有几个比它小的数。用康托展开式可以求3*3这个图的状态(我理解为状态压缩成一个值)。
思路:需要用到广搜,如果从给定状态到0去搜,将输入的X当成9,与相邻数字交换值(因为9是在游戏中空格),四个方向广搜 ,多组数据会超时,所以可以用0去广搜所有的状态,并且记录路径(这一点不太好理解)相当于打表。
注意:由于是从0向其他状态搜的,所以输出的所有方向都是相反的!
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
int f[10]= {1,1,1,1,1,1,1,1,1},book[402879];
char book1[402879],net[6]= {'l','r','d','u'};//方向相反
int dir[4][2]= {0,1,0,-1,-1,0,1,0}; //r l u d
struct node
{
int st,x,y,e[5][5];//当前状态的值 9 的坐标 状态图
};
int kangtuo(int a[])//1-> 求康托展开式的值
{
int l,sum=0;
for(int i=1; i<=9; i++)
{
l=0;
for(int j=i+1; j<=9; j++)
{
if(a[i]>a[j])
l++;
}
sum+=(f[9-i]*l);
}
return sum;
}
void bfs()
{
queue<node>q;
node u,v;
u.x=3,u.y=3;
u.st=0;//初始状态 康托展开式的值为零
int k=1;
for(int i=1; i<=3; i++)
for(int j=1; j<=3; j++)//初始图
u.e[i][j]=k++;
book[0]=0;//标记状态 同时记录路径
q.push(u);
while(!q.empty())
{
u=q.front();
q.pop();
for(int i=0; i<4; i++)
{
v=u;
int tx=u.x+dir[i][0];
int ty=u.y+dir[i][1];
if(ty>=1&&ty<=3&&tx>=1&&tx<=3)
{
int t1=u.e[tx][ty],t2=u.e[u.x][u.y];
v.e[tx][ty]=t2,v.e[u.x][u.y]=t1;//交换
int k=1,s[11];
for(int w=1; w<=3; w++)
for(int j=1; j<=3; j++)
s[k++]=v.e[w][j];
v.st=kangtuo(s);//康托展开
if(book[v.st]==-1)//该状态没有走过
{
v.x=tx;
v.y=ty;
book[v.st]=u.st;//记录路径
book1[v.st]=net[i];//标记路径 由上一个状态怎么来的(记录方向)
q.push(v);
}
}
}
}
}
int main()
{
int k;
memset(book1,'\0',sizeof(book1));
memset(book,-1,sizeof(book));
for(int i=1; i<=9; i++)
{
k=1;
for(int j=1; j<=i; j++)
k=k*j;
f[i]=k;
}//阶乘
bfs();
char p[50];int MAP[12];
memset(p,'\0',sizeof(p));
while(gets(p))
{
if(p[0]=='E'&&p[1]=='O'&&p[2]=='F')
break;
int k=1;
for(int i=0;p[i]!='\0';i++)
{
if(p[i]==' ')
continue;
if(p[i]=='x')
MAP[k++]=9;
else MAP[k++]=p[i]-'0';
}
int n=kangtuo(MAP);
if(book[n]!=-1)
{
while(n!=0)
{
printf("%c",book1[n]);
n=book[n];
}
}
else printf("unsolvable");
printf("\n");
memset(p,'\0',sizeof(p));
}
return 0;
}