八数码问题
/*8数码问题,即在一个3×3的矩阵中有8个数(1至8)和一个空格,从一个状态转换到另一个状态,
每次只能移动与空格相邻的一个数字到空格当中
AOJ-417-8数码
http://icpc.ahu.edu.cn/OJ/Problem.aspx?id=417
这题是求转化的最少步数,可用BFS解决,共有9!=362880种情况,
关键是如何标记已经访问过的状态,保证每次搜索得到的状态都是最小的步数,
这里可将字符串转化成对应的整数来处理,可用康托展开来节省存储空间
康托展开: X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
ai为在当前未出现的数字中是排在第几个(0<=ai<i)
例如3 5 7 4 1 2 9 6 8 展开为
X=2*8!+3*7!+4*6!+2*5!+0*4!+0*3!+2*2!+0*1!+0*0!=98884
*/
#include "iostream" #include<string.h> using namespace std; int a[363000][9]; //共有9!=362880种情况,用char存储节省空间 int goal[9]; int visit[363000]; int dis[363000]; int dir[4][2]={{-1,0},{1,0},{0,1},{0,-1}}; int step[363000]; //从上一步来的方向 int pre[363000]; //从上一步来的状态 char name[4][10]={"upper ","down ","right ","left "}; int c[9]={1,1,2,6,24,120,720,5040,40320}; // i! int find(int str[9]) //将字符串转换成一个整数 { int i,j,k; int f[10]; int sum=0; memset(f,0,sizeof(f)); for(i=0;i<9;i++) //康托展开式 9个数字相加 所以9次循环 { k=0; for(j=0;j<8;j++) { //最高是8! 所以8次循环 if(j<str[i]&&!f[j]) k++; } f[str[i]]=1; sum+=k*c[8-i]; } return sum; } int bfs() { int i,j,t,flag; int head,tail; int x,y,z; int nx,ny,nz; int p,q,temp; string s=""; memset(dis,0,sizeof(dis)); //到每种状态的做小步数 memset(visit,0,sizeof(visit)); //标记过的点不能重复走 t=find(a[0]); visit[t]=1; head=0; tail=1; memset(step,0,sizeof(step)); step[0]=pre[0]=-1; while(head<tail) { flag=1; for(i=0;i<9;i++) if(a[head][i]!=goal[i]) //和目标状态相同即停止搜索 { flag=0; break; } if(flag) //打印路劲 { int h=head; temp=0; while(head) { p=pre[head]; //前一步的状态 q=step[head]; //从上一步来的方向 for里面的i s+=name[q]; head=p; } cout<<s<<endl; return dis[h]; //当前最短的次数 } for(i=0;i<9;i++) //找到0所在位置 if(a[head][i]==0) { x=i/3; //x行y列 y=i%3; z=i; //找出原来空格的位置 存到z break; } for(i=0;i<4;i++) { nx=x+dir[i][0]; ny=y+dir[i][1]; nz=nx*3+ny; //重点记住 nz即为将赋予新的空格的位置 ,但是目前为非0 if(0<=nx&&nx<3&&0<=ny&&ny<3) { for(j=0;j<9;j++) //枚举 1-9 进队列 a[tail][j]=a[head][j]; //因为每次只交换2个,所以先把以前的复制给下一个,再进行交换 a[tail][z]=a[head][nz]; //做一次移动,即非0元素和0交换 a[tail][nz]=0; t=find(a[tail]); if(!visit[t]) //如果该状态已经走过,就不要进队了 { visit[t]=1; dis[tail]=dis[head]+1; //有数组保存当前每一步的步数 pre[tail]=head; //从上一步来的状态 //因为是队列 //所以每次列入队列,并不是连续的 step[tail]=i; //从上一步来的方向 tail++; } } } head++; } return -1; } int main() { int i,ans; for(i=0;i<9;i++) cin>>a[0][i]; for(i=0;i<9;i++) cin>>goal[i]; ans=bfs(); if(ans==-1) cout<<"Impossible\n"; else cout<<ans; return 0; }