八數碼問題簡單解決辦法
阿新 • • 發佈:2021-08-15
問題分析:
八數碼問題是一個經典的BFS問題,把棋局看成一個狀態圖,共有9!種狀態。從初始棋局開始,每次轉移到下個狀態,直到目標棋局為止。
仔細分析可知,八數碼的關鍵是判重,如果不去除重複狀態,程式會產生很多無效狀態,從而複雜度大大增加
解決演算法:
BFS + Cantor
案例分析:
(0表示空格所在位置)
初始棋局:
|1|2|3|
|0|8|4|
|7|6|5|
目標棋局:
|1|0|3|
|8|2|4|
|7|6|5|
1.先將空格和8交換得到:
|1|2|3|
|8|0|4|
|7|6|5|
2.再將空格和2交換得到目標棋局:
|1|0|3|
|8|2|4|
|7|6|5|
總共執行兩次操作
C++程式碼:
#include <bits/stdc++.h> using namespace std; const int LEN = 362880; // 共9!種狀態 struct node { int state[9]; // 記錄八數碼排列,即一個狀態 int dis; }; int dir[4][2] = { //左,上,右,下順時針方向 {-1,0}, {0,-1}, {1,0}, {0,1}, }; int visited[LEN] = {0}; // cantor判重,若某狀態訪問過置為一 int start[9]; int goal[9]; long factory[] = {1,1,2,6,24,120,720,5040,40320,362880}; // cantor判重用到的常數,從0!到9! bool cantor(int str[], int n) { long result = 0; for(int i=0; i<n; ++i) { int cnt = 0; for(int j=i+1; j<n; ++j) if(str[i] > str[j]) cnt++; result += cnt*factory[n-i-1]; } if(!visited[result]) { visited[result] = 1; return true; } else return false; } int bfs() { node head; memcpy(head.state, start, sizeof(head.state)); head.dis = 0; queue<node> q; cantor(head.state, 9); q.push(head); while(!q.empty()) { head = q.front(); q.pop(); int z; for(z=0; z<9; ++z) if(head.state[z] == 0) //尋找元素0的位置 break; // z的二維轉換 int x = z%3; int y = z/3; // 向四個方向轉移新狀態 for(int i=0; i<4; ++i) { int nx = x + dir[i][0]; int ny = y + dir[i][1]; int nz = ny*3 + nx; // 二維化一維 if(nx >= 0 && nx <3 && ny >= 0 && ny < 3) { //未越界 node nnode; memcpy(&nnode, &head, sizeof(struct node)); swap(nnode.state[z], nnode.state[nz]); nnode.dis++; if(memcmp(nnode.state, goal, sizeof(goal)) == 0) //與目標狀態比較 return nnode.dis; if(cantor(nnode.state, 9)) //判重 q.push(nnode); //把新的狀態放進佇列 } } } return -1; //沒找到 } int main() { //freopen("in.txt", "r", stdin); for(int i=0; i<9; ++i) cin >> start[i]; for(int i=0; i<9; ++i) cin >> goal[i]; int num = bfs(); if(num != -1) cout << num << endl; else cout << "impossible" << endl; return 0; }