1043 八數碼問題 康託展開 + 反向 bfs +記錄路徑
阿新 • • 發佈:2018-12-16
bfs 剪枝要點
visit
陣列 hash函式(康託展開)記憶化
bfs 打表儲存
所有可達路徑優先佇列
periority queue多點同時bfs
反向bfs
+ bfs 打表儲存所有路徑
stl + 正向bfs
9! 一共有 362880 種可能,10e5 資料規模並不是非常大,所以我考慮用 map<string , bool>
來作為visit。map 的訪問和插入都是 log n
所以對於每一個輸入的序列,時間複雜度都是 n*log n
log 362880
約等於 20 ,所以總的時間複雜度等於 36288020 = 7257600 , 71e6,對單個testcase 是可接受的。
如果再考慮 有多個 test case
,13個testcase
就會達到 1e8
的時間複雜度,顯然是不可接受的。
正向bfs 加 hash判重
此處要引入一個定理 康託展開和逆康託展開
康託展開是一個全排列到一個自然數的雙射,常用於構建hash表時的空間壓縮。設有n個數(1,2,3,4,…,n),可以有組成不同(n!種)的排列組合,康託展開表示的就是是當前排列組合在n個不同元素的全排列中的名次。
(轉自此處)
顯然如果我們此處使用康託展開作 hash 對映,visit 陣列就可以從 一個 map
退化成一個 一維陣列
時間複雜度是 n
同上
但是這麼寫的話,會有很多次重複的操作
反向bfs + 記憶化 + hash 判重
由於樹的結構特點,一個子節點一定只有一個父節點。 所以可以直接從根節點開始便利,使用鏈式前向星的思想,對每個節點,記錄該節點的父節點。
然後只需要進行一次bfs 搜尋即可。 程式碼如下:
#include<iostream> #include<queue> #include<cstring> #include<string> #include<vector> using namespace std; struct node { int pre; int op; string s; int x; int y; }; node path[400000]; int vis[400000]; int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}}; char ss[4] = { 'u','d','l','r'}; int fac[] = {1,1,2,6,24,120,720,5040,40320,362880}; //i的階乘為fac[i] int Cantor(string s){ int sum = 0; for(int i = 0; i < 9; i++){ int num = 0; for(int j = i+1; j < 9; j++){ if(s[j] < s[i]) num++;//確定當前元素i在未出現的元素中是第幾個(從小到大) } sum += fac[8-i] * num; } return sum; } void bfs() { int counts =0; path[counts].pre = -1; path[counts].s = "123456780"; path[counts].x=2; path[counts].y=2; queue<int> qu; qu.push(counts); int visn; visn = Cantor(path[counts].s); vis[visn] =counts; counts++; while(!qu.empty()) { int f = qu.front(); qu.pop(); int x = path[f].x; int y = path[f].y; char tp; for(int i=0;i<4;i++) { int px = x+dir[i][0]; int py = y+dir[i][1]; if(px<0||px>2||py<0||py>2)continue; tp = path[f].s[px*3+py]; // 開始轉換 path[f].s[px*3+py] = path[f].s[x*3+y]; path[f].s[x*3+y] = tp; visn = Cantor(path[f].s); //判斷是否出現過 if(vis[visn]!=-1) { tp = path[f].s[px*3+py]; // 回溯 path[f].s[px*3+py] = path[f].s[x*3+y]; path[f].s[x*3+y] = tp; continue; } vis[visn] =counts; path[counts].op = i; path[counts].pre = f; path[counts].s = path[f].s; path[counts].x = px; path[counts].y = py; qu.push(counts); counts++; tp = path[f].s[px*3+py]; //回溯 path[f].s[px*3+py] = path[f].s[x*3+y]; path[f].s[x*3+y] = tp; } } } int main() { for(int i=0;i<400000;i++)vis[i]=-1; bfs(); char st1[30]; string st; ios::sync_with_stdio(false); vector<int> re; while(cin.getline(st1,25)) { st=""; re.clear(); for(int i=0;i<17;i+=2) { if(st1[i]=='x')st = st+'0'; else st= st+st1[i]; } int viss; viss = Cantor(st); if(vis[viss]!=-1) { int flag=vis[viss]; while(path[flag].pre!=-1) { cout<<ss[path[flag].op]; flag = path[flag].pre; }cout<<endl; } else cout<<"unsolvable"<<endl; } return 0; }