http://poj.org/problem?id=1077
#include<cstdio> //双向BFS poj 1077 16ms
#include<cstring>
#include<queue>
#include <iostream>
using namespace std;
const int V=362882;
struct nod{
int n,x; //n表示康拓展开的值,x表示9的位置。
int m[9]; //记录状态
};
int fac[]={1,1,2,6,24,120,720,5040,40320}; //阶乘表
int dx[]={1,-1,0,0},dy[]={0,0,-1,1},tr[]={1,0,3,2}; //tr用于转向,就是正向遍历 <-> 逆向遍历
char dir[]="dulr";
int vir[2][V],p[V],fa[V],d[V],re,rs,ko;
queue<nod> q[2]; //两个队列
void bfs(nod st,nod ed);
int exp(nod s,int k);
int cato(nod a);
/*
void print(int s[]){
for(int i=1;i<=9; i++){
printf("%d ", s[i-1]);
if(i%3==0) puts("");
}
}
void invKT(int n, int k){
int s[10];
int t,j;
bool visit[10] = {false}; //需要记录该数是否已在前面出现过
for(int i=0; i<n; i++){
t = k/fac[n-i-1];
for(j=0; j<n; j++){
if(!visit[j]){
if(t == 0) break;
t--;
}
}
s[i] = j;
visit[j] = true;
k %= fac[n-i-1];
//cout << j << ",";
}
print(s);
cout << endl;
}
*/
int main()
{
freopen("in.txt","r",stdin);
char str[30]; int i;
nod st,ed;
while(gets(str)!=NULL)
{
int cnt=0;
for(i=0;i<9;i++) st.m[i]=i+1; //读取初始的起点和终点
for(i=0;i<strlen(str);i++){
if(str[i]=='x') ed.x=cnt, ed.m[cnt++]=9;
if(str[i]>='1'&&str[i]<='8') ed.m[cnt++]=str[i]-'0';
}
ed.n=cato(ed);
st.x=8; st.n=cato(st);
//cout << st.n << endl;
ko=-1,cnt=0;
bfs(st,ed); //双向BFS搜索
int tempP[V];
if(ko!=-1){ //ko表示是交点的方向,如果没改变说明没交点
for(i=re;fa[i]!=i;i=fa[i]){
tempP[cnt] = i;
d[cnt++]=p[i]; //下面几行打印路径
}
for(i=cnt-1;i>=0;i--){
printf("%c",dir[d[i]]);
//invKT(9,tempP[i]);
}
printf("%c",dir[ko]);
for(i=rs;i!=st.n;i=fa[i]) printf("%c",dir[tr[p[i]]]);
}
else printf("unsolvable");
printf("\n");
}
return 0;
}
int cato(nod a) //康拓展开
{
int i,j,k,sum=0;
for(i=0;i<9;i++){
for(j=i+1,k=0;j<9;j++) if(a.m[j]<a.m[i]) k++;
sum+=k*fac[8-i];
}
return sum;
}
void bfs(nod st,nod ed)
{
memset(fa,-1,sizeof(fa));
memset(vir,0,sizeof(vir));
q[0].push(st); q[1].push(ed); //起点终点入队
vir[0][st.n]=1; vir[1][ed.n]=1;
fa[st.n]=st.n; fa[ed.n]=ed.n;
while(!q[0].empty()||!q[1].empty())
{
nod ss=q[0].front(); q[0].pop();
nod ee=q[1].front(); q[1].pop();
if(exp(ss,0)||exp(ee,1)) break;
}
}
int exp(nod s,int k) //扩展函数,即对s点寻找下一层的点
{
int i,t,sn=s.x,sx=sn/3,sy=sn%3,nn;
nod nex; //下一个遍历节点
for(i=0;i<4;i++){
int nx=sx+dx[i],ny=sy+dy[i];
if(nx<3&&nx>=0&&ny<3&&ny>=0){
nex=s;
nn=nx*3+ny;
t=nex.m[sn]; nex.m[sn]=nex.m[nn]; nex.m[nn]=t; //交换两个位置的值
nex.n=cato(nex);
nex.x=nn; //求下一个可能扩展的点
if(vir[k][nex.n]) continue; //比重复遍历
if(vir[k^1][nex.n]==1) { //找到交点返回1,k代表是从起点还是从终点的路径
// 判断是正向还是逆向
if(k) { ko=i; re=s.n; rs=nex.n; }
else { ko=tr[i]; re=nex.n; rs=s.n; }
return 1;
}
vir[k][nex.n]=1;
q[k].push(nex); //可扩展,入队。
fa[nex.n]=s.n; //保存路径
p[nex.n]=i; //保存路径的方向
}
}
return 0;
}