Eight
Descriptions:
簡單介紹一下八數碼問題:
在一個3×3的九宮格上,填有1~8八個數字,空余一個位置,例如下圖:
1 | 2 | 3 |
4 | 5 | 6 |
7 | 8 |
在上圖中,由於右下角位置是空的,你可以移動數字,比如可以將數字6下移一位:
1 | 2 | 3 | 1 | 2 | 3 | |
4 | 5 | 6 | → | 4 | 5 | |
7 | 8 | 7 | 8 | 6 |
或者將數字8右移一位:
1 | 2 | 3 | 1 | 2 | 3 | |
4 | 5 | 6 | → | 4 | 5 | 6 |
7 | 8 | 7 | 8 |
1~8按順序排列的情況稱為“初始狀態”(如最上方圖)。“八數碼問題”即是求解對於任意的布局,將其移動至“初始狀態”的方法。
給定一個現有的九宮格布局,請輸出將它移動至初始狀態的移動方法的步驟。
Input
輸入包含多組數據,處理至文件結束。每組數據占一行,包含8個數字和表示空位的‘x’,各項以空格分隔,表示給定的九宮格布局。
例如,對於九宮格
1 | 2 | 3 |
4 | 6 | |
7 | 5 | 8 |
輸入應為:1 2 3 x 4 6 7 5 8
Output
對於每組輸入數據,輸出一行,即移動的步驟。向上、下、左、右移動分別用字母u、d、l、r表示;如果給定的布局無法移動至“初始 狀態”,請輸出unsolvable。
如果有效的移動步驟有多種,輸出任意即可。
Sample Input
2 3 4 1 5 x 7 6 8
Sample Output
ullddrurdllurdruldr
題目鏈接
https://vjudge.net/problem/HDU-1043
其實就是反向bfs,不過用了一個新的方法去存放拼圖序列,康托展開即把拼圖(x12345678)全排列,再用數字去表示,簡而言之就是用不同的數字去代替拼圖序列,使之跟簡單、快速的搜索
不會康托展開不關系,點擊下面鏈接,現學現會
https://www.cnblogs.com/sky-stars/p/11216035.html
AC代碼
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 362880+5//876543210的hash值為362880 即最多出現362880種可能 using namespace std; static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; // 階乘 struct node { string path;//路徑 int hashs;//hash值 int pos;//0的位置 }; node now,net; queue<node>q; int dt[][4]= {{1,0},{-1,0},{0,1},{0,-1}};//四個方向 char op[5]="udlr";//這個與上面的搜索方向是反的,因為是反向bfs int tmp[9];//臨時存儲拼圖的序列 int result=46234;//123456780 最終答案的hash值 string path[Maxn];//path[x] hash值為x的路徑 int vis[Maxn];//vis[x] hash值為x的拼圖序列是否標記過 //康托展開 int cantor(int *a) { int x = 0; for (int i = 0; i < 9; ++i) { int smaller = 0; // 在當前位之后小於其的個數 for (int j = i + 1; j < 9; ++j) { if (a[j] < a[i]) smaller++; } x += FAC[9 - i - 1] * smaller; // 康托展開累加 } return x+1; // 康托展開值 } //逆康托展開 void decantor(int x, int *a) { vector<int> v; // 存放當前可選數 for(int i=0; i<9; i++) v.push_back(i); for(int i=0; i<9; i++) { int r = x % FAC[9-i-1]; int t = x / FAC[9-i-1]; x = r; sort(v.begin(),v.end());// 從小到大排序 a[i]=v[t]; // 剩余數里第t+1個數為當前位 v.erase(v.begin()+t); // 移除選做當前位的數 } } void bfs() { MEM(vis,0);//初始化 for(int i=0; i<8; i++)//tmp一開始為123456780,從這開始打散拼圖 tmp[i]=i+1; tmp[8]=0; now.pos=8; now.hashs=result; now.path=""; path[result]=""; vis[result]=1; q.push(now); while(!q.empty()) { now=q.front(); q.pop(); for(int i=0; i<4; i++)//四個方向 { int tx=now.pos/3+dt[i][0]; int ty=now.pos%3+dt[i][1]; if(tx>=0&&ty>=0&&tx<=2&&ty<=2)//沒走出去拼圖 { net=now; net.pos=tx*3+ty; decantor(now.hashs-1,tmp);//求tmp swap(tmp[now.pos],tmp[net.pos]);//得到新的tmp net.hashs=cantor(tmp);//得到新tmp對應的hash if(!vis[net.hashs])//這都bfs老套路了 沒啥說的 { vis[net.hashs]=1; net.path=op[i]+net.path; q.push(net); path[net.hashs]=net.path; } } } } return; } int main() { bfs(); char x; while(cin>>x)//輸入格式 沒啥說的 { if(x=='x') { now.pos=0; tmp[0]=0; } else { tmp[0]=x-'0'; } for(int i=1; i<9; i++) { cin>>x; if(x=='x') { now.pos=i; tmp[i]=0; } else { tmp[i]=x-'0'; } } now.hashs=cantor(tmp);//求出tmp這個拼圖序列的hash值 if(!vis[now.hashs])//這個hash沒標記過,即沒產生過這個拼圖序列 cout<<"unsolvable"<<endl; else cout<<path[now.hashs]<<endl;//輸出hash的路徑 } return 0; }