八數碼簡介
八數碼問題也稱爲九宮問題。在3×3的棋盤,擺有八個棋子,每個棋子上標有1至8的某一數字,不一樣棋子上標的數字不一樣樣。棋盤上另外一個空格,與空格相鄰的棋子可以移到空格中。要求解決的問題是:給出一個初始狀態和一個目標狀態,找出一種從初始轉變成目標狀態的移動棋子步數最少的移動步驟。所謂問題的一個狀態就是棋子在棋盤上的一種擺法。棋子移動後,狀態就會發生改變。解八數碼問題實際上就是找出從初始狀態到達目標狀態所通過的一系列中間過渡狀態。ios
求解八數碼問題要懂得的知識
1.康託展開,八數碼在交換的過程當中狀態會改變,康託展開用於求出某一格局的狀態數。唐託展開公式:X=a[n](n-1)!+a[n-1](n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!其中a[i]爲當前未出現的元素中是排在第幾個(從0開始),而且0<=a[i]<i(1<=i<=n)數組
2.逆序數,經過求初始格局和目標格局逆序數,而後在比較二者的逆序數的奇偶性是否相同,若是奇偶性相同,則能夠從初始格局變到目標格局。不然,不可達。逆序數求法:假設有n個元素的排列,a[i]爲排列中的元素(0<=i<n),求在排列中比a[i]小的數的和。好比有這麼一個排列:54120,5後面比它小的數有3個(0不算,後同);4後面比它小的數有2個;1後面比它小的數有0個;2後面比它的小的數有0個;因此,該排列的逆序數=3+2+0+0=5.測試
3.在八數碼中0位置的數與它相鄰的上下左右的位置的數交換不會影響這個格局的逆序數的奇偶性。好比有如下格局:spa
4 | 6 | 7 |
---|---|---|
5 | 8 | 1 |
2 | 3 | 0 |
若是2這個數和5這個數交換就會致使格局的逆序數的奇偶性的變化,而若是0和1交換就不會致使奇偶性的變化。咱們要保證移動前和移動後逆序數的奇偶性不改變,用一維數組來存儲格局,要注意索引。code
廣度優先搜索求解八數碼問題代碼
#include <iostream> #include <string> #include <cstring> #include <cmath> #include <vector> #include <queue> #include <set> using namespace std; #define N 9 int jc[N+1]={1,1,2,6,24,120,720,5040,40320,362880};//0-9的階乘 typedef struct data { int arr[N];//格局 int hash;//存儲某一格局的哈希 int pos;//0當前位置 int step;//記錄步數 }Node; int dir[4][2]={ {0,1}, {1,0}, {0,-1}, {-1,0} }; /** * 康託展開 */ int cantor(int arr[N]) { int i,j; int sum=0; for( i=0;i<N;i++) { int nmin=0; for(j=i+1;j<N;j++) { if(arr[i]>arr[j]) nmin++; } sum+=(nmin*jc[N-i-1]); } return sum; } /** *數據交換 */ void swap(int *arr,int i,int j) { int t=arr[i]; arr[i]=arr[j]; arr[j]=t; } /** * 打印數組,測試用 */ void printArray(int * arr) { int i,j; for (i=0;i<N;i++) { if(i%3==0) cout<<"\n"; cout<<arr[i]<<" "; } cout<<endl; } /** * 複製數組 */ void copyArray(int src[N],int target[N]) { int i; for(i=0;i<N;i++) { target[i]=src[i]; } } /** * 廣搜 */ int bfs(int arr[N],int sHash,int tHash) { if(sHash==tHash) return 0; int i,j; queue<Node> q; set<int> setHash; Node now,next; copyArray(arr,now.arr); int pos=0; for (i=0;i<N;i++) { if(arr[i]==0) break; pos++; } now.hash=sHash; now.step=0; next.step=0; now.pos=pos; q.push(now); setHash.insert(now.hash); while(!q.empty()) { now=q.front(); q.pop(); for (i=0;i<4;i++) { int offsetX=0,offsetY=0; offsetX=(now.pos%3+dir[i][0]); offsetY=(now.pos/3+dir[i][1]); if(offsetX>=0&&offsetX<3&&offsetY<3&&offsetY>=0) { copyArray(now.arr,next.arr);//每次換方向,就複製 next.step=now.step; next.step++; swap(next.arr,now.pos,offsetY*3+offsetX); next.hash=cantor(next.arr); next.pos=(offsetY*3+offsetX); int begin=setHash.size(); setHash.insert(next.hash); int end=setHash.size(); if(next.hash==tHash){ return next.step; } if(end>begin) { q.push(next); } } } } return -1; } /** *求逆序數 */ int inversion(int arr[N]) { int sum=0; for(int i=0;i<N;++i) { for(int j=i+1;j<N;++j) { if(arr[j]<arr[i]&&arr[j]!=0)//不與0比較 { sum++; } } } return sum; } int main(int argc, char **argv) { int i,j; string s="123456780"; string t="123456078"; int is[N],it[N];//源int數組和目標int數組 for (i=0;i<9;i++) { if (s.at(i)>='0'&&s.at(i)<='8') { is[i]=s.at(i)-'0'; }else { is[i]=0; } if (t.at(i)>='0'&&t.at(i)<='8') { it[i]=t.at(i)-'0'; }else { it[i]=0; } } int sHash,tHash;//源哈希和目標哈希 sHash=cantor(is); tHash=cantor(it); int inver1=inversion(is);//求初始格局的逆序數 int inver2=inversion(it);//求目標格局的逆序數 if((inver1+inver2)%2==0) { int step=bfs(is,sHash,tHash); cout<<step<<endl; } else { cout<<"沒法從初始狀態到達目標狀態"<<endl; } return 0; }