題意:就是一個八數碼問題,求從初始狀態得到解的路徑。
思路: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");
}