P1379 八數碼難題-啟發式搜尋
阿新 • • 發佈:2018-12-22
- P1379 八數碼難題
- 題意:在3×3的棋盤上,擺有八個棋子,每個棋子上標有1至8的某一數字。棋盤中留有一個空格,空格用0來表示。空格周圍的棋子可以移到空格中。要求解的問題是:給出一種初始佈局(初始狀態)和目標佈局(為了使題目簡單,設目標狀態為123804765),找到一種最少步驟的移動方法,實現從初始佈局到目標佈局的轉變。
- 思路:迭代加深就是每次限制搜尋深度 ,這樣可以在整個搜尋樹深度很大而答案深度又很小的情況下大大提高效率
- 使k從1開始不斷加深列舉 ,作為最大步數進行迭代加深搜尋判斷
- A*估價函式設定為 :
- 當前狀態還有多少個位置與目標狀態不對應,若當前步數+估價函式值>列舉的最大步數 則直接返回
- 最優性剪枝:
- 顯然當前列舉下一個狀態時如果回到上一個狀態肯定不是最優,
- 所以我們在列舉下一狀態時加入對這種情況的判斷即 pre + i==3時 正好對應著回上一個狀態,進行剪枝。
-
#include<bits/stdc++.h> using namespace std; int ans[5][5]= {{0,0,0,0},{0,1,2,3},{0,8,0,4},{0,7,6,5}}; int cur[5][5],k,flag,sx,sy; char str[15]; int to[5][4]= {{1,0},{0,-1},{0,1},{-1,0}}; bool check() { for(int i=1; i<=3; i++) for(int j=1; j<=3; j++) if(ans[i][j]!=cur[i][j]) return 0; return 1; } bool ok(int step) { int cnt=0; for(int i=1; i<=3; i++) for(int j=1; j<=3; j++) if(ans[i][j]!=cur[i][j]) if(++cnt+step>k)return 0; return 1; } void dfs(int id,int x,int y,int pre) { if(id==k) { if(check())flag=1; return; } if(flag)return; for(int i=0; i<4; i++) { int tx=x+to[i][0]; int ty=y+to[i][1]; if(tx<1||ty<1||ty>3||tx>3||pre+i==3)continue; swap(cur[x][y],cur[tx][ty]); if(!flag&&ok(id)) dfs(id+1,tx,ty,i); swap(cur[tx][ty],cur[x][y]); } } int main() { scanf("%s",str); for(int i=0; i<9; i++) { cur[i/3+1][i%3+1]=str[i]-'0'; if(str[i]-'0'==0)sx=i/3+1,sy=i%3+1; } if(check())printf("0\n"); else { while(++k) { dfs(0,sx,sy,-1); if(flag) { printf("%d\n",k); break; } } } return 0; }