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<stdio.h>#include<string.h>#include<stdlib.h>char a[363000][9]; //共有9!=362880種情況,用char儲存節省空間的char goal[9];char visit[363000];int dis[363000];int dir[4][2]={{-1,0},{1,0},{0,1},{0,-1}};int c[9]={1,1,2,6,24,120,720,5040,40320};int find(char str[9]) //將字串轉換成一個整數{ int i,j,k; int f[10]; int sum=0; memset(f,0,sizeof(f)); for(i=0;i<9;i++) { k=0; for(j=0;j<8;j++) 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;memset(dis,0,sizeof(dis)); //到每種狀態的做小步數 memset(visit,0,sizeof(visit)); //標記過的點不能重複走 t=find(a[0]);visit[t]=1;head=0;tail=1; while(head<tail){flag=1;for(i=0;i<9;i++)if(a[head][i]!=goal[i]) //和目標狀態相同即停止搜尋{flag=0;break;}if(flag)return dis[head];for(i=0;i<9;i++) //找到0所在位置if(a[head][i]==0){x=i/3;y=i%3;z=i;break;} for(i=0;i<4;i++){nx=x+dir[i][0];ny=y+dir[i][1];nz=nx*3+ny;if(0<=nx&&nx<3&&0<=ny&&ny<3){for(j=0;j<9;j++)a[tail][j]=a[head][j];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;tail++;}}}head++;}return -1;}int main(){int i,ans;for(i=0;i<9;i++)scanf("%d",&a[0][i]);for(i=0;i<9;i++)scanf("%d",&goal[i]);ans=bfs();if(ans==-1)printf("Impossible\n"); else printf("%d\n",ans); return 0;}
POJ-1077-Eight
http://poj.org/problem?id=1077
這題不需要求最小步數,要求移動的路勁,和上題差不多,用BFS,再記錄一下路勁,最後反向輸出即可
#include<stdio.h>#include<string.h>#include<stdlib.h>char a[363000][9]; //共有9!=362880種情況,用char儲存節省空間的char goal[9];char visit[363000];int dis[363000];int step[363000]; //從上一步來的方向int pre[363000]; //從上一步來的狀態int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; char name[4]={'d','u','r','l'};int c[9]={1,1,2,6,24,120,720,5040,40320};void init(){int i;for(i=0;i<8;i++)goal[i]=i+1;goal[8]=0;}int find(char str[9]) //將字串轉換成一個整數{ int i,j,k; int f[10]; int sum=0; memset(f,0,sizeof(f)); for(i=0;i<9;i++) { k=0; for(j=0;j<8;j++) 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 p,q,temp;int x,y,z;int nx,ny,nz;char sol[5000];memset(dis,0,sizeof(dis)); //到每種狀態的做小步數 memset(visit,0,sizeof(visit)); //標記過的點不能重複走memset(step,0,sizeof(step)); t=find(a[0]);visit[t]=1;step[0]=pre[0]=-1;head=0;tail=1; while(head<tail){flag=1;for(i=0;i<9;i++)if(a[head][i]!=goal[i]) //和目標狀態相同即停止搜尋{flag=0;break;}if(flag) //列印路勁{temp=0; while(head){p=pre[head]; //前一步的狀態q=step[head]; //從上一步來的方向sol[temp++]=name[q];head=p;}for(i=temp-1;i>=0;i--)printf("%c",sol[i]);printf("\n");return 1;} for(i=0;i<9;i++) //找到0所在位置if(a[head][i]==0){x=i/3;y=i%3;z=i;break;} for(i=0;i<4;i++){nx=x+dir[i][0]; //移動0元素ny=y+dir[i][1];nz=nx*3+ny;if(0<=nx&&nx<3&&0<=ny&&ny<3){for(j=0;j<9;j++)a[tail][j]=a[head][j];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,k,ans;char ss[50];k=0; gets(ss);for(i=0;i<strlen(ss);i++){if('0'<=ss[i]&&ss[i]<='9')a[0][k++]=(ss[i]-'0');else if(ss[i]=='x')a[0][k++]=0;}init();ans=bfs();if(ans==-1)printf("unsolvable\n"); return 0;}