藍橋杯 歷屆試題 九宮重排(bfs)
阿新 • • 發佈:2019-01-30
問題描述 如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。
我們把第一個圖的局面記為:12345678.
把第二個圖的局面記為:123.46758
顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。
本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。輸入格式 輸入第一行包含九宮的初態,第二行包含九宮的終態。輸出格式 輸出最少的步數,如果不存在方案,則輸出-1。樣例輸入12345678.
123.46758樣例輸出3樣例輸入13524678.
46758123.樣例輸出
我們把第一個圖的局面記為:12345678.
把第二個圖的局面記為:123.46758
顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。
本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。輸入格式 輸入第一行包含九宮的初態,第二行包含九宮的終態。輸出格式 輸出最少的步數,如果不存在方案,則輸出-1。樣例輸入12345678.
123.46758樣例輸出3樣例輸入13524678.
46758123.樣例輸出
22
【解題思路】
這道題用了bfs+康拓判重
bfs既廣度優先搜尋,搜到的第一個就是最少步數。如何判斷重複是一個問題,有很多人用雜湊表雜湊函式,本人學藝不精不會使用雜湊(捂臉),康拓展開是數學上的一個公式,這個公式運用於判斷一個數在該數全排列下第幾個。
定義:X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
eg:數213在123的全排列下面是第X+1個數
2的後面有1個數比2小,既1*2!
1的後面沒有比1小的數,既0*1!
3的後面沒有比3小的數,既0*0!
X = 1*2!+0*1!+0*0! = 2
123的全排列: 123 132 213 231 312 321
九宮最多有9!種排列組合,既362880中排列組合方式,使用陣列state[362880]可以用來判斷是否出現重複的元素。
使用bfs搜尋需要使用佇列這一資料結構,曾嘗試過使用STL中的queue但是失敗了(捂臉),下面程式碼使用得是佇列先進先出的特性。
#include<iostream> #include<string> #include<cstring> using namespace std; int state[362880]; //判斷是否重複 int step[362880]; //儲存步數 int bnum,bkt; int dx[] = {-1,1,0,0}; //上下左右 int dy[] = {0,0,-1,1}; typedef int type[9]; type q[362880]; type a,b; //a為初始狀態,b為最終狀態 type tem,tem2; int kangtuo(int x[]) //康拓展開判斷是否重複 { int fac[]={1,1,2,6,24,120,720,5040,40320}; int i,j,t,sum; sum = 0; for(i=0;i<9;i++) { t = 0; for(j=i+1;j<9;j++) { if(x[j]<x[i]) t++; } sum = sum+t*fac[8-i]; } if(state[sum]==1) return 0; else { state[sum] = 1; return 1; } } int bfs() { //bfs廣度優先搜尋,搜尋到的第一個就是最短路徑 memcpy(q[0],a,sizeof(a)); //初始狀態入隊 int front = 0; step[front] = 0; int rear = 1; int i,j,p,x,y,xx,yy; //type tem,tem2; while(front < rear) //如果佇列不為空 { memcpy(tem,q[front],sizeof(tem)); if(memcmp(tem,b,sizeof(b)) == 0) //如果隊首元素與最終元素相等 return front; for(i=0;i<9;i++) //找到0的位置 { if(tem[i] == 0) break; } x = i/3; //轉換成二維座標 y = i%3; for(j=0;j<4;j++) //進行空格上下左右移動 { xx = x+dx[j]; yy = y+dy[j]; if(xx>=0 && yy>=0 && xx<3 && yy<3) //如果移動沒有越界 { p = xx*3+yy; memcpy(tem2,tem,sizeof(tem)); //將tem拷貝到tem2上面 tem2[i] = tem2[p]; //交換p和i的值,既移動0的位置 tem2[p] = 0; if(kangtuo(tem2)) { memcpy(q[rear],tem2,sizeof(tem2)); step[rear] = step[front]+1; rear++; } } } front++; } return -1; } int main() { memset(state,0,sizeof(state)); int i,anum; string str1,str2; cin>>str1; cin>>str2; for(i=0;i<9;i++) //將'.'轉換成0 { if(str1[i]>'0'&&str1[i]<='9') a[i] = str1[i]-'0'; else { a[i] = 0; anum = i; } } for(i=0;i<9;i++) { if(str2[i]>'0'&&str2[i]<='9') b[i] = str2[i]-'0'; else { b[i] = 0; bnum = i; } } int road = bfs(); //根據輸出最後結果 if(road == -1) cout<<road<<endl; else cout<<step[road]<<endl; return 0; }
最後提一句,重排九宮就是經典bfs演算法中的八數碼,這裡有收藏的八數碼的八種境界,適合新手看一下
http://blog.csdn.net/kopyh/article/details/48442415#t7