[kuangbin帶你飛]專題二 搜尋進階
阿新 • • 發佈:2020-09-17
[kuangbin帶你飛]專題二 搜尋進階
A - Eight HDU - 1043
這一題就是經典八數碼。演算法難點在於:
1.判重:map,hash,cantor康託展開
2.搜尋:bfs dbfs A*
我是直接暴力預處理,從12345678X開始進行所有情況處理,然後結果反序輸出。節點用int儲存9位數的序列。
#include <iostream> #include <queue> #include <map> #include <algorithm> using namespace std; map<int,stringView Code> ans; int n[10]; struct node{ int num,nine; node(int nn,int nni):num(nn),nine(nni) {}; }; int change(int a,int b,int c){ int u = c/n[a]%10, v = c/n[b]%10; //cout<<a<<" "<<b<<" "<<c<<endl; //cout<<u<<" "<<v<<" "<<u*n[a]<<" "<<v*n[b]<<" "<<v*n[a]<<" "<<u*n[b]<<endl;return c - u*n[a] - v*n[b] + v*n[a] + u*n[b]; } void init(){ n[8] = 1;for(int i=7;i>=0;i--) n[i] = n[i+1]*10; //for(int i=0;i<9;i++) cout<<n[i]<<endl; queue<node> q; ans[123456789] = ""; q.push(node(123456789,8)); while(!q.empty()){ node p= q.front();q.pop(); int num = p.num , nine = p.nine; int tnt; //--------------------------------------------- switch(nine){ case 0: tnt = change(0,1,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,1)); } tnt = change(0,3,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,3)); } break; case 1: tnt = change(1,0,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,0)); } tnt = change(1,2,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,2)); } tnt = change(1,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,4)); } break; case 2: tnt = change(2,1,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,1)); } tnt = change(2,5,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,5)); } break; case 3: tnt = change(3,0,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,0)); } tnt = change(3,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,4)); } tnt = change(3,6,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,6)); } break; case 4: tnt = change(4,1,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,1)); } tnt = change(4,3,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,3)); } tnt = change(4,5,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,5)); } tnt = change(4,7,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,7)); } break; case 5: tnt = change(5,2,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,2)); } tnt = change(5,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,4)); } tnt = change(5,8,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "u"; q.push(node(tnt,8)); } break; case 6: tnt = change(6,3,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,3)); } tnt = change(6,7,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,7)); } break; case 7: tnt = change(7,6,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,6)); } tnt = change(7,4,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,4)); } tnt = change(7,8,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "l"; q.push(node(tnt,8)); } break; case 8: tnt = change(8,5,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "d"; q.push(node(tnt,5)); } tnt = change(8,7,num); if(ans.count(tnt)==0){ ans[tnt] = ans[num] + "r"; q.push(node(tnt,7)); } break; default:cout<<nine<<endl; } //--------------------------------------------- } } int main(){ init(); char _; while(cin>>_){ int cnt=0; if(_=='x') _='9'; cnt+=(_-'0')*n[0]; for(int i=1;i<9;i++) { cin>>_; if(_=='x') _='9'; cnt+=(_-'0')*n[i]; } //cout<<cnt<<endl; //cout<<ans[cnt]<<endl; if(ans.count(cnt)==0) puts("unsolvable"); else{ string strng = ans[cnt]; //cout<<strng<<endl; for(int i=strng.length()-1;i>=0;i--){ cout<<strng[i]; }cout<<endl; } } return 0; }
這裡是要求是給出 起點和終點序列 然後得出最短操作 而且相同長度要求最小字典順序(重點,我這裡wrong了很多次 看題目才知道)
第一遍:就是A* + map 嘗試 然後 timelimit。
第二遍:看其他人程式碼,有兩種,一種就是設立limit的dfs,
另一種就是預處理 ,這裡很巧妙 我想了一早上 想通了;重點 x 是操作單位不能動,其他字元是可替換的。
舉例子:645X312 ---->456X123 的操作 和 123X456---->231X564的操作一樣 為什麼?因為這只是簡單字元替換 ,這種替換並不影響結果。甚至645X312可以替換成qweXyui,不影響的。
然後就是12345678X,1234567X8....9種X在不同位置的預處理,然後通過給出起點X位置進行得出結果。
重點 要 {dlru}這樣進行遍歷 因為需要最小字典序列
#include <iostream> #include <queue> #include <cstring> #include <string> using namespace std; const int M = 400000; int vis[9][M]; int pre[9][M]; //----------------------------------------------------------- void show(int *a){ for(int i=0;i<9;i++) cout<<a[i];cout<<endl; } //------------------------------------------------------------ int changeId[9][4] = {{3,-1,1,-1},{4,0,2,-1},{5,1,-1,-1}, {6,-1,4,0},{7,3,5,1},{8,4,-1,2}, {-1,-1,7,3},{-1,6,8,4},{-1,7,-1,5}}; char d[5] = {'d','l','r','u'}; //康託展開====================================================== const int fk[10] = {1,1,2,6,24,120,720,5040,40320,362880}; int cantor(int *a){ int ans = 0; for(int i=0;i<8;i++){ int small = 0; for(int j=i+1;j<9;j++) if(a[j]<a[i]) small++; ans += small*fk[8-i]; } return ans; } //---------------------------------------------------------- struct node{ int num[9],zero,hashcode; }; void bfs(int x){ node now; for(int i=0,j=1;i<9;i++) if(i==x) now.num[i] = 0; else now.num[i] = j++; now.zero=x; now.hashcode = cantor(now.num); queue<node> q; q.push(now); vis[x][now.hashcode] = 1; while(!q.empty()){ node p = q.front();q.pop(); int zero = p.zero,hashcode = p.hashcode; for(int i=0;i<4;i++){ if(changeId[zero][i] == -1) continue; node temp = p; temp.zero = changeId[zero][i]; swap(temp.num[zero],temp.num[temp.zero]); temp.hashcode = cantor(temp.num); if(vis[x][temp.hashcode]==-1){ vis[x][temp.hashcode] = i; pre[x][temp.hashcode] = hashcode; q.push(temp); } } } } //預處理================================================= void init(){ memset(vis,-1,sizeof vis); memset(pre,-1,sizeof pre); for(int i=0;i<9;i++) bfs(i); } //--------------------------------------------------------- int main(){ init(); int _,op=1; scanf("%d",&_); while(_--){ //---------------------- int k; char a[10],b[10]; int c[10],u[10]; scanf("%s%s",a,b); //printf("%s\n%s\n",a,b); //進行符號兌換:c陣列為兌換陣列,得到兌換後的目標陣列和X位置======== for(int i=0,j=1;i<9;i++) if(a[i]=='X') k=i; else c[a[i]-'0'] = j++; for(int i=0;i<9;i++) if(b[i]=='X') u[i] = 0; else u[i] = c[b[i]-'0']; //show(u); //=============================== int can = cantor(u); string ans = ""; while(can != -1){ ans = d[vis[k][can]] + ans; can = pre[k][can]; } printf("Case %d: %d\n",op++,ans.length()-1); cout<<ans.substr(1)<<endl; } return 0; }View Code