题目大意:给出八数码的起点状态,求出能够到达到终点状态的步数的序列(不一定是要最短的步数)
终点状态为:1 2 3
4 5 6
7 8 x
思路:这道题有很多方法可以做,在这里我用的是A*算法。在保存每一步的具体路径的时候我们可以用康托展开。首先把每一位赋予一个权值,个位的权值是0!,十位是1!,百位是2!,千位是3!........然后统计当前数位在他后面比他小的数字有多少个,然后乘上当前数位的权值,最后再把每个数位得到的值加起来就是康托展开了。
例如序列 1 2 3 4 5 6 7 8 0 的展开:1后面比它小的有0,2后面比他小的有0。。。。因此该序列的康托展开 =0*0!+1*1!+1*2!+1*3!+1*4!+1*5!+1*6!+1*7!+1*8!
又如 4312 的展开 0*0!+0*1!+2*2!+3*3!
此题还有一个比较坑的地方就是如何保存路径的问题,我们可以在结构体中开一个长整型变量,把它视作4进制的,0代表向上走,1代表向下。。。具体参考我的代码
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#define LL long long
#define MAXN 370000
using namespace std;
int bv[10] = {1,1,2,6,24,120,720,5040,40320,362880};
char st[10],nd[10] = {"123456780"};
int dd[4] = {-3,3,-1,1};
char iindex[4] = {'u','d','l','r'};
int abs(int x){return x>0?x:-x;}
struct node
{
char s[10];//当前状态
int step;//当前深度
int diff;//估值函数
int pos;//当前0的位置
LL path;//保存路径
node(){}
node(char *a,int b,int c,int d,LL e)
{
strcpy(s,a);
step = b;
diff = c;
pos = d;
path = e;
}
bool operator < (const node &a) const
{
return step + diff > a.step + a.diff;
}
};
int Cantor(char *a)//康托展开
{
int res = 0;
for(int i = 0; i < 9; i++)
{
int cnt = 0;
for(int j = i + 1; j < 9; j++)
if(a[i] > a[j]) cnt++;
res += bv[8-i]*cnt;
}
return res;
}
int G(char *a)//A*算法估值函数
{
int res = 0;
for(int i = 0; i < 9; i++)
if(a[i]!='0'&&a[i]!=i+'1')
res += (abs(i - (a[i] - '1'))/3 + abs(i - (a[i] - '1'))%3);
return res;
}
void getpath(LL p,int steps)//输出路径
{
string str="";
int res[100];
memset(res,0,sizeof res);
int res_cnt = 0;
for(int i = 1; i <= steps; i++)
{
res[++res_cnt] = p%4;
p/=4;
}
for(int i = res_cnt; i >= 1; i--)
str+=iindex[res[i]];
cout<<str<<endl;
}
bool vis[MAXN];
int _pos;
void astar()
{
memset(vis,0,sizeof vis);
priority_queue<node> myque;
int _diff = G(st);
vis[Cantor(st)] = 1;
myque.push(node(st,0,_diff,_pos,0));
while(!myque.empty())
{
node now = myque.top();
myque.pop();
char ss[10];
strcpy(ss,now.s);
int hash;
if(strcmp(ss,nd) == 0)//找到终点
{
getpath(now.path,now.step);
return;
}
for(int i = 0; i < 4; i++)
{
int tpos = now.pos + dd[i];
if(tpos > 8||tpos < 0||(tpos%3 != now.pos%3&&tpos/3 != now.pos/3)) continue;
swap(ss[tpos],ss[now.pos]);
hash = Cantor(ss);
if(!vis[hash])//剪枝,避免重复
{
int nowdiff = G(ss);
vis[hash] = 1;
myque.push(node(ss,now.step+1,nowdiff,tpos,now.path*4+(LL)i));//最后一个变量更新路径
}
swap(ss[tpos],ss[now.pos]);
}
}
//printf("unsolvable\n");
}
bool check()//统计逆序对
{
int cnt = 0;
for(int i = 0; i < 9; i++)
for(int j = i+1; j < 9; j++)
if(st[i] > st[j] && st[j] != '0') cnt++;
if((cnt%2) == 0) return 1;
return 0;
}
int main()
{
char x;
while(scanf("%c",&x) != EOF)
{
memset(st,0,sizeof st);
int cnt=0;
if(isdigit(x)) st[cnt++]=x;
else if(x=='x') st[cnt++]='0',_pos=cnt-1;
while(scanf("%c",&x)&&x!='\n')
{
if(isdigit(x)) st[cnt++]=x;
else if(x=='x') st[cnt++]='0',_pos=cnt-1;
}
if(!check())//判断逆序对,剪枝
{
printf("unsolvable\n");
continue;
}
astar();
}
}