这道题刚开始单纯的用BFS做,从初始状态搜到12345678x,因为有很多组输入,妥妥的TLE
后来试了下双向BFS,还是TLE,(没做好优化,优化好的话是可以卡时间过去的)
最好最简单的解法是,从12345678x开始搜索,打表记录路径和移动,这样搜索一次,就把能到达的状态还有路径全部记录下来了,再输入时直接查询输出就可以,很快
状态压缩的话需要用一种编码方式,将012345678的全排列对应为0~362880之间的数,具体操作看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int dm[]={-3,3,-1,1};
const char mop[]={'u','d','l','r'}; //这里的移动是与dm对应的
const char mop0[]={'d','u','r','l'}; //这里是反向的移动,用来保存打表
const int ten[]={1000000000,100000000,10000000,1000000,100000,10000,1000,100,10,1};
bool vis[362880]; //访问标记
int f[362880]; //保存父节点
char op[362880]; //保存移动
int move(int n,int i) //移动
{
int pos=9,tmp=n;
while(pos>1)
{
if(n%10==0) break;
n/=10;
pos--;
}
if(pos%3==1&&i==2) return -1;
if(pos%3==0&&i==3) return -1;
if(pos<4&&i==0) return -1;
if(pos>6&&i==1) return -1;
int np=pos+dm[i];
int t=tmp%ten[np-1]/ten[np];
tmp+=ten[pos]*t;
tmp-=ten[np]*t;
return tmp;
}
int fact[9]; //编码用
void init() //初始化
{
fact[0]=1;
for(int i=1;i<9;i++)
fact[i]=fact[i-1]*i;
memset(vis,false,sizeof(vis));
}
int get_code(int s) //转码
{
int code=0;
int st[9];
for(int i=8;i>=0;i--,s/=10)
st[i]=s%10;
for(int i=0;i<9;i++)
{
int cnt=0;
for(int j=i+1;j<9;j++)
if(st[j]<st[i]) cnt++;
code+=fact[8-i]*cnt;
}
return code;
}
bool insert(int code) //检查是否访问过
{
if(vis[code]) return false;
return vis[code]=true;
}
int sta;
bool input() //输入
{
char a[10];
bool flag=false;
int i=0;
sta=0;
while(~scanf("%s",a))
{
sta*=10;
if(a[0]!='x')
sta+=a[0]-'0';
i++;
if(i==9) break;
}
if(i==9)
flag=true;
return flag;
}
int main()
{
int sta0=123456780;
init();
queue<int> q;
q.push(sta0);
insert(get_code(sta0));
memset(f,-1,sizeof(f));
while(!q.empty()) //从初始状态开始BFS,打表
{
int u=q.front();//cout<<1<<" "<<u<<endl;
q.pop();
int codu=get_code(u);
for(int i=0;i<4;i++)
{
int v=move(u,i);
if(v==-1) continue;
int code=get_code(v);
if(insert(code))
{
q.push(v);
f[code]=codu; //记录父节点
op[code]=mop0[i]; //记录到父节点的操作
}
}
}
while(input())
{
int code=get_code(sta);
queue<char> s0;
if(vis[code]) //被访问过,可以到达
{
while(f[code]!=-1) //寻找父节点,输出移动操作
{
printf("%c",op[code]);
code=f[code];
}
}
else printf("unsolvable");
printf("\n");
}
return 0;
}