关键思想:关于这个问题,有大牛给出了八境界,我先介绍一种比较容易理解的。BFS+康托展开。
康托展开其实就是把一个排列和一个整数对应起来,比如我们把空格当作9,那八数码的每个格局就是9的全排列中的某一个排列,经过康托展开可以把这个排列映射成一个整数,123456789是第一个排列,987654321是最后一个排列.
考虑到题目的输出是多组的,那我们其实可以把所有状态都从123456789开始BFS遍历出来,组成一个图,总共不过9!,40w+状态,BFS可以搞定。
那BFS的结点该怎么构造呢?我本来想着只用hash值,前驱,和空白格的位置来表示,不愿保存格局,太小气了。后来想象其实每次状态转移都要用到这个格局的,解码也需要时间,没有必要,所以一个结点要保存的信息应该有这样几个:当前格局,空白格位置(省的每次都要遍历一下找空白格)。关键思想就是这样,接下来就是耐心地实现了。
我们要弄清楚,BFS获得的图是一棵树,这棵树每个结点对应一个可达的格局(一个额外规律是他们逆序数都是偶数),并且每个格局都是不同的,所有非目标格局出现的位置在离目标格局最近的位置。
代码如下:
#include <iostream> #include <cstdio> #include <algorithm> #include <vector> #include <queue> #include <string.h> using namespace std; const int MAXS=366666; int fac[11]={1,1,2,6,24,120,720,5040,40320,362880,362880};//康托展开用,阶乘数组 int dir[4][2]={0,1,0,-1,1,0,-1,0}; //方向控制 bool vis[MAXS]; int from[MAXS][2];//记录路径 :0为前驱结点,1为方向 struct node{ int space; char a[9];//格局 node(){} }; int EnKT(char* s){//康托展开 int ans=1,cnt; for(int i=0;i<9;i++){ cnt=0; for(int j=i+1;j<9;j++){//找到后面比较小的数,他们在i处的排列已经历数过 if(s[j]<s[i])cnt++; } ans+=cnt*fac[8-i]; } return ans; } void BFS(){//BFS预处理 //变量声明 node nw,nt; queue<node>q; int Cont; //目标状态入队 for(int i=0;i<9;i++)nw.a[i]='0'+i+1; nw.space=8; Cont=EnKT(nw.a); vis[Cont]=true; from[Cont][0]=Cont;//目标格局自反 q.push(nw); while(!q.empty()){ nw=q.front(); q.pop(); for(int i=0;i<4;i++){ int x=nw.space/3+dir[i][0],y=nw.space%3+dir[i][1]; if(x>=0&&x<=2&&y>=0&&y<=2){ //生成next结点 nt.space=3*x+y; memcpy(nt.a,nw.a,9*sizeof(char)); swap(nt.a[nt.space],nt.a[nw.space]); Cont=EnKT(nt.a); if(!vis[Cont]){ from[Cont][0]=EnKT(nw.a),from[Cont][1]=i;//记录路径 q.push(nt); vis[Cont]=true; } } } } return ; } int main(){ char tmp[30],start[9]; BFS(); while(gets(tmp)){ for(int i=0,cnt=0;i<strlen(tmp);i++){ if(tmp[i]<='9'&&tmp[i]>='1') start[cnt++]=tmp[i]; else if(tmp[i]=='x')start[cnt++]='9'; } int Cont=EnKT(start); if(vis[Cont]){//如果状态图中有它 int i=Cont; while(i!=from[i][0]){//输出时要反着来哦, if(from[i][1]==0)printf("l"); else if(from[i][1]==1)printf("r"); else if(from[i][1]==2)printf("u"); else if(from[i][1]==3)printf("d"); i=from[i][0]; } printf("\n"); }else{ printf("unsolvable\n"); } } return 0; }