hdu1043 A*演算法 八數碼問題
阿新 • • 發佈:2018-12-02
題意大致就是讓你復原一個八數碼拼圖,輸出具體路徑
思路:這裡使用的是A*演算法,A*演算法可能比較陌生,迪傑斯克拉法求最短路相比大家都比較熟悉,A*演算法就是他的進化,或者說dij演算法是A*演算法中把預估函式看為0得到的演算法,
A*演算法的主體是一個路徑計算函式:f=g+h,其中g表示的從起始點到某一點的耗費距離,h則表示的是從當前點到目標點的預估耗費,一般在搜圖中使用曼哈頓距離,而在本題中則是計算八數碼中每個點復原所耗費的最小距離。
然後就跟bfs一樣的模板了
程式碼:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cmath> #include<queue> using namespace std; const int maxn = 4e5 + 10; struct node { int f[3][3]; int x, y; int h, g; int hashnum; bool operator <(const node a)const { return g + h > a.g + a.h; } }; struct path { int pre; char c; }pre[maxn]; int ha[9] = {40320,5040,720,120,24,6,2,1,1}; int dir[4][2] = { {-1,0},{1,0},{0,-1},{0,1} }; char d[10] = "udlr"; int vis[maxn]; void print(int x)//因為你是先找,所以要先倒溯到起始點然後輸出路徑 { if (pre[x].pre == -1) return; print(pre[x].pre); printf("%c", pre[x].c); } int gethash(node e)//利用康拓展卡來進行雜湊表。 { int a[9]; int cnt = 0, k = 0;; int ans = 0; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) a[cnt++] = e.f[i][j]; for (int i = 0; i < 9; i++) { k = 0; for (int j = i + 1; j < 9; j++) { if (a[i] > a[j]) k++; } ans += k * ha[i]; } return ans; } int geth(node e)//計算A*演算法中的h函式 { int ans = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if(e.f[i][j]) ans += abs(i - (e.f[i][j] - 1) / 3) + abs(j - (e.f[i][j] - 1) % 3); } } return ans; } void astar(node e) { int t = 0; memset(vis, 0, sizeof(vis)); node a; int cnt = 1, xx, yy; for (int i = 0; i < 9; i++) a.f[i / 3][i % 3] = (i + 1) % 9; int end = gethash(a); e.hashnum = gethash(e); e.g = 0, e.h = geth(e); vis[e.hashnum] = 1; pre[e.hashnum].pre = -1; if (e.hashnum == end) { printf("\n"); return; } priority_queue<node>que; que.push(e); while(!que.empty()) { e=que.top(); que.pop(); for(int i=0;i<4;i++) { xx=e.x+dir[i][0]; yy=e.y+dir[i][1]; if(xx<0||yy<0||xx>=3||yy>=3)continue; a=e; swap(a.f[e.x][e.y],a.f[xx][yy]); int k=gethash(a); if(vis[k])continue; vis[k]=1; a.hashnum=k; a.x=xx; a.y=yy; a.g++; a.h=geth(a); pre[k].pre=e.hashnum; pre[k].c=d[i]; if(k==end) { print(k); printf("\n"); return ; } que.push(a); } } } int main() { string a; while (getline(cin, a)) { node e; int n = a.size(); for (int i = 0, j = 0; i < n; i++) { if (a[i] == ' ') continue; if (a[i] == 'x') { e.x = j / 3; e.y = j % 3; e.f[j / 3][j % 3] = 0; } else e.f[j / 3][j % 3] = a[i] - '0'; j++; } int k = 0; for (int i = 0; i < 9; i++)//計算這個八數碼中的逆序數,如果逆序數為奇數則判定為不可解決,(從題意中可得出) { if (e.f[i/3][i%3] == 0) continue; for (int j = 0; j < i; j++) { if (e.f[j/3][j%3] == 0) continue; if (e.f[j/3][j%3] > e.f[i/3][i%3]) k++; } } if (k & 1) printf("unsolvable\n"); else astar(e); //a.clear(); } //system("system"); return 0; }