藍橋杯【歷屆試題】九宮重排
如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。
我們把第一個圖的局面記為:12345678.
把第二個圖的局面記為:123.46758
顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。
本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。
輸入格式
輸入第一行包含九宮的初態,第二行包含九宮的終態。
輸出格式
輸出最少的步數,如果不存在方案,則輸出-1。
樣例輸入
12345678.
123.46758
樣例輸出
3
樣例輸入
13524678.
46758123.
樣例輸出
22
題目思路:
我們可以把每一種局面當作是一個結點。空白格可以向四個方向移動,移動後的局面是新的結點。顯然,我們可以通過bfs來搜尋目標局面,並且一旦搜尋到就一定是最少移動次數。我們需要解決的問題有兩個。
1:如何記錄每一種局面。
2:如何判斷當前局面是否已經出現過,即之前的搜尋過程中已經搜尋過了,簡單點說就是判重。
問題1解決方法:
我們可以通過宣告二維陣列,int a[maxstate][9], a[1][9]表示第一種局面,a[2][9]表示第二種局面,這裡我們為了表示方便,typedef int State[9]; State st[maxstate];這樣的話,第i種局面就可以用st[i]表示。
問題2解決方法:
非常直接的方法宣告陣列vis[9][9][9][9][9][9][9][9][9],這樣的一個九維陣列,顯然,這樣做並不是那麼合理的。這裡所用的方法是雜湊對映。
備註:
1:memcmp();memcpy(); memset();
2:我們把3*3的九宮格的數字按照一行一行的從左往右的順序處理成一行資料。假設在這一行資料中他的位置是n,那麼他在二維表中的位置就是(n/3, n%3)。
題目程式碼:
#include<cstdio> #include<cstring> #include<string> #include<iostream> using namespace std; string str1,str2; typedef int State[9]; int maxstate = 1000000; State st[1000000] ,goal; int dist[1000000]; int dx[4] = {0,0,1,-1}; int dy[4] = {1,-1,0,0}; //--- const int hashsize = 1000003; int head[hashsize], next[hashsize]; int hash(State& s) { int v = 0; for(int i=0; i<9; i++) v = v*10 + s[i]; //把9個數字組合成9位數 return v % hashsize; //確保hash函式值是不超過hash表的大小的非負整數 } int try_to_insert(int s) { int h = hash(st[s]); int u = head[h]; //從表頭開始查詢連結串列 while(u){ if(memcmp(st[s], st[u], sizeof(st[s]))==0) return 0; //有重複,插入失敗 u = next[u]; //順著連結串列繼續找 } next[s] = head[h]; //該結點插入到連結串列中 head[h] = s; return 1; } void init_table() { memset(head, 0, sizeof(head)); } int bfs() { init_table(); //初始化查詢表 int front = 1; int rear = 2; while(front < rear){ State& s = st[front]; if(memcmp(goal, s, sizeof(s)) == 0) return front; int z; for(int i=0; i<9; i++) if(!s[i]) z = i; //找“0”的位置 int x = z/3, y = z%3; for(int i=0; i<4; i++){ int newx = x + dx[i]; int newy = y + dy[i]; int newz = newx * 3 + newy; if(newx>=0 && newx<3 && newy>=0 && newy<3){ State& t = st[rear]; memcpy(&t ,&s ,sizeof(s)); //擴充套件新結點 t[newz] = s[z]; t[z] = s[newz]; dist[rear] = dist[front] + 1; if(try_to_insert(rear)) rear++; //查重 修改隊尾指標 } } front++; //修改隊首指標 } return 0; } int main() { cin>>str1>>str2; for(int i=0; i<9; i++) { if(str1[i]=='.') st[1][i] = 0; else st[1][i] = str1[i] - '0'; } for(int i=0; i<9; i++){ if(str2[i]=='.') goal[i] = 0; else goal[i] = str2[i] - '0'; } int ans = bfs(); if(ans == 0) printf("%d\n",-1); else printf("%d\n",dist[ans]); return 0; }