poj 1077 Eight (八數碼問題——A*+cantor展開+奇偶剪枝)
阿新 • • 發佈:2018-08-30
www. += 優先級 pri 排列 view 組成 esp 改變
題目來源:
http://poj.org/problem?id=1077
題目大意:
給你一個由1到8和x組成的3*3矩陣,x每次可以上下左右四個方向交換。求一條路徑,得到12345678x這樣的矩陣。若沒有路徑,則輸出unsolvable。
經典的八數碼問題。
這題我用A*算法做的。推薦一篇博客,從大體上介紹了一下啟發式算法的代表A*算法:
https://www.cnblogs.com/zhoug2020/p/3468167.html
說說這道題的幾個註意點:
首先就是判重的問題,搜索的狀態是九個數(含x),開個九重數組也不是不可以,但用cantor展開hash一下還是方便的。所謂cantor展開,就是對1..n的所有排列,唯一對應一個1..n!的值。代碼中的cantor展開,可以當做模板用。
其次就是奇偶剪枝了。對於1..n的排列來說,每次對換兩個元素,排列逆序數的奇偶性一定改變。而容易發現,和x交換,1..8的排列逆序數奇偶性是不變的。故可以實現判斷是否是unsolvable。
最後就是Astar算法本身了:
首先需要一個優先隊列。由於對優先級的需要,我們需要在結構體中對<運算符進行重載(詳見代碼)。
然後結構體中存儲路徑最好還是用string吧,畢竟vector還是沒它方便。
好像就是這麽多。搜索這種東西,復雜度比較難把握,所以能優化的地方盡量優化一下吧。比如搜索方向,感覺還是先搜索down和right比較好,雖然可能效果不太明顯。
這道題還要繼續做。hdu1043還沒A掉,雙向BFS,還有什麽打表方法還沒有嘗試寫呢。加上!!!,表示要記得做!
#include<iostream> #include<cstdio> #include<string> #include<queue> #include<cstring> using namespace std; char buf[30]; int puz[9]; int fac[]={1,1,2,6,24,120,720,5040,40320,362880}; int cantor(int s[]) { int sum=0; for(int i=0;i<9;i++) { int num=0;View Codefor(int j=i+1;j<9;j++) if(s[j]<s[i]) num++; sum+=(num*fac[9-i-1]); } return sum+1; } struct tnode { int puz[9]; string path; int loc;//0的位置 int status;//cantor展開值 int n;//搜索深度 int f;//估值函數 int getloc() { for(int i=0;i<9;i++) if(puz[i]==0) return i; } bool operator<(const tnode& y) const { return f>y.f; } }; int vis[363000]; int dis[9]={4,3,2,3,2,1,2,1,0}; int a[4]={1,3,-1,-3}; char b[5]="rdlu"; int aim=46234; int main() { while(scanf("%[^\n]",buf)!=EOF) { getchar(); for(int i=0,cnt=0;buf[i]!=‘\0‘;i++) { if(buf[i]==‘x‘) puz[cnt++]=0;//用0存儲x else if(buf[i]>=‘1‘&&buf[i]<=‘8‘) puz[cnt++]=buf[i]-‘0‘; } //for(int i=0;i<9;i++) printf("%d ",puz[i]); printf("\n"); //printf("%d\n",cantor(puz)); int cnt=0; for(int i=0;i<9;i++) if(puz[i]!=0) { for(int j=i+1;j<9;j++) if(puz[j]!=0&&puz[j]<puz[i]) cnt++; } if(cnt%2==1) { printf("unsolvable\n"); continue; } priority_queue<tnode> q; memset(vis,0,sizeof(vis)); tnode node1; memcpy(node1.puz,puz,sizeof(puz)); node1.path=""; node1.loc=node1.getloc(); node1.status=cantor(node1.puz); node1.n=0; node1.f=node1.n+dis[node1.loc]; if(node1.status==aim)//入隊之前先判斷,可以加快速度 { cout<<node1.path<<endl; continue; } q.push(node1); vis[node1.status]=1; bool flag=false; while(!q.empty()) { tnode node2=q.top();q.pop(); bool fflag=false; for(int i=0;i<4;i++) { tnode node3=node2; //printf("%d ",node3.loc); if(a[i]==1&&node3.loc%3<=1) { node3.puz[node3.loc]=node3.puz[node3.loc+a[i]]; node3.puz[node3.loc+a[i]]=0; node3.status=cantor(node3.puz); } else if(a[i]==-1&&node3.loc%3>=1) { node3.puz[node3.loc]=node3.puz[node3.loc+a[i]]; node3.puz[node3.loc+a[i]]=0; node3.status=cantor(node3.puz); } else if(a[i]==3&&node3.loc<=5) { node3.puz[node3.loc]=node3.puz[node3.loc+a[i]]; node3.puz[node3.loc+a[i]]=0; node3.status=cantor(node3.puz); } else if(a[i]==-3&&node3.loc>=3) { node3.puz[node3.loc]=node3.puz[node3.loc+a[i]]; node3.puz[node3.loc+a[i]]=0; node3.status=cantor(node3.puz); } //printf("%d ",node3.status); if(!vis[node3.status]) { node3.path+=b[i]; node3.loc=node3.getloc(); node3.n++; node3.f=node3.n+dis[node3.loc]; q.push(node3); vis[node3.status]=1; } if(node3.status==aim) { node1=node3; fflag=true; break; } } if(fflag) { flag=true; break; } } if(flag) { cout<<node1.path<<endl; } else { printf("unsolvable\n"); } } return 0; }
poj 1077 Eight (八數碼問題——A*+cantor展開+奇偶剪枝)