题意:就是一个八数码问题,求从初始状态得到解的路径。
思路:bfs,对于每个状态通过康拓展开来记录判重。
康拓展开:设一个状态a[1...n],其康拓展开X=b[n]*(n-1)!+b[n-1]*(n-2)!+...+b[i]*(i-1)!+....+b[1]*0!;其中b[i]表示在a[i+1]到a[n]中<a[i]的个数。
逆康拓展开:因为b[i]<i,所以(n-1)!>b[n-1]*(n-2)!+...+b[i]*(i-1)!+....+b[1]*0!,从而根据康拓展开的公式,可得从i=1到i=n,b[i]=num/((i-1)!),num=num%((i-1)!),通过b[i]即可得知a[i]在a序列里还未出现的数中排第b[i]+1个。
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<stdlib.h>
using namespace std;
int jc[9]={1,1,2,6,24,120,720,5040,40320};
int change[4]={1,-1,-3,3};//控制方向
bool pd[362881]={};
map < int , int > l;//存路径
string S;
stack <int> s;
struct node
{
int a[10];
int pos;
};
queue <node> q;
int getcontor(int* p)//求康拓展开
{
int temp;
int ans=0;
for(int i=8;i>=1;--i)
{
temp=0;
for(int j=i+1;j<=9;++j)
if(p[i]>p[j])
++temp;
ans+=(temp*jc[9-i]);
}
return ans;
}
inline void Swap(int& x1,int& x2)
{
int t;
t=x1;
x1=x2;
x2=t;
}
void print(int num)
{
int b[10]={0,1,2,3,4,5,6,7,8,9};
int posi=9;
while(l[num]!=-1)
{
s.push(l[num]);
Swap(b[posi],b[posi-change[l[num]]]);
posi-=change[l[num]];
num=getcontor(b);
}
int x;
while(!s.empty())
{
x=s.top();
switch(x)
{
case 0:printf("r");break;
case 1:printf("l");break;
case 2:printf("u");break;
case 3:printf("d");break;
default: break;
}
s.pop();
}
}
int main()
{
node x,y;
for(int i=1;i<10;++i)
{
cin>>S;
if(S[0]=='x')
{
S[0]='9';
x.pos=i;
}
x.a[i]=(int)(S[0]-'0');
}
int num;
num=getcontor(x.a);
pd[num]=true;
l[num]=-1;
q.push(x);
bool flag=false;
while(!q.empty())
{
x=q.front();
q.pop();
y=x;
for(int i=0;i<4;++i)
{
if(i==0&&(x.pos==3||x.pos==6||x.pos==9))continue ;
if(i==1&&(x.pos==1||x.pos==4||x.pos==7))continue ;
y.pos=x.pos+change[i];
if(y.pos<1||y.pos>9)continue ;
Swap(y.a[x.pos],y.a[y.pos]);
num=getcontor(y.a);
if(!pd[num])
{
pd[num]=true;
l[num]=i;
q.push(y);
if(num==0)flag=true;
}
Swap(y.a[x.pos],y.a[y.pos]);
}
if(flag)break;
}
if(pd[0])
print(0);
else
printf("unsolvable\n");
}