1043(逆向搜尋bfs+康拓展開)
阿新 • • 發佈:2019-01-02
題意:給出一個初始的八數碼的狀態,然後問經過那些步驟可以轉化到八數碼的排列狀態
題解:首先需要了解一下康拓展開是個啥,那麼進入傳送門(大神的程式碼可以先不看),然後瞭解了這個黑科技後呢,就得考慮如何實現的問題了,多組資料,如果對於每一個狀態我們都"正向"進行一次bfs,那麼豈不是時間要爆炸,仔細思考一番,不如逆向搜尋出每種狀態,每個狀態算出康拓展開值,並標記方向(逆著),通過康拓展開值進行標記父子關係,最後對於每組資料,只需要算出此時的康拓展開值,如果有,輸出答案,沒有輸出無解即可。
附上長長的程式碼:
#include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; const int maxn=3700000;//考慮下八數碼的最大康拓展開值 struct node1{ char way; int fath; }; node1 Node[maxn]; struct node2{ int aa[10],n,val; }; int dir[][2]={{0,1},{-1,0},{0,-1},{1,0}}; int fac[10]; //康拓展開板子 void set_fac() { fac[0]=1; for(int i=1;i<=8;i++){ fac[i]=fac[i-1]*i; } } int cantor(int aa[]) { int ans=0,k; for(int i=0;i<9;i++){ k=0; for(int j=i+1;j<9;j++){ if(aa[i]>aa[j]){ k++; } } ans+=k*fac[8-i]; } return ans; } void bfs(int a[]) { queue<node2>Q; node2 temp1; for(int i=0;i<9;i++){ temp1.aa[i]=a[i]; } temp1.n=8; temp1.val=0;//可以設最終狀態的康拓展開值為0 Node[temp1.val].fath=0; Q.push(temp1); while(!Q.empty()){ temp1=Q.front(); Q.pop(); for(int i=0;i<4;i++){ node2 temp2=temp1; int tx=temp1.n/3+dir[i][0]; int ty=temp1.n%3+dir[i][1]; if(tx>=0&&ty>=0&&tx<3&&ty<3){ temp2.n=tx*3+ty; swap(temp2.aa[temp2.n],temp2.aa[temp1.n]); temp2.val=cantor(temp2.aa); if(Node[temp2.val].fath==-1){ Node[temp2.val].fath=temp1.val; if(i==0){Node[temp2.val].way='l';} if(i==1){Node[temp2.val].way='d';} if(i==2){Node[temp2.val].way='r';} if(i==3){Node[temp2.val].way='u';} Q.push(temp2); } } } } } int main() { set_fac(); int a[10]; for(int i=0;i<9;i++){ a[i]=i+1; } for(int i=0;i<370000;i++){ Node[i].fath=-1; } bfs(a); char ch[50]; int ss[10]; while(gets(ch)>0){ for(int i=0,j=0;ch[i]!=0;i++){ if(ch[i]=='x'){ ss[j++]=9; }else if(ch[i]>='0'&&ch[i]<='8'){ ss[j++]=ch[i]-'0'; } } int s=cantor(ss); if(Node[s].fath==-1){ printf("unsolvable\n"); continue; } while(s!=0){ printf("%c",Node[s].way); s=Node[s].fath; } printf("\n"); } return 0; } /* //確保下方向是否寫的無誤 1 2 3 4 5 x 7 8 6 1 2 3 4 5 6 7 x 8 */