主要搜尋過程:
建立兩個表,OPEN表儲存所有已產生而未考察的節點,CLOSED表中記錄已訪問過的節點。
遍曆當前節點的各個節點,將n節點放入CLOSE中,取n節點的子節點X,->算X的估價值->
While(OPEN!=NULL)
{
從OPEN表中取估價值f最小的節點n;
if(n節點 in CLOSE) continue;
if(X節點==目標節點)break;//全部子節點
else
{
if(X in OPEN) 比較兩個X的估價值f//注意是同一個節點的兩個不同路徑的估價值
if( X的估價值小於OPEN表的估價值 )
更新OPEN表中的估價值; //取最小路徑的估價值??
if(X in CLOSE) 比較兩個X的估價值//注意是同一個節點的兩個不同路徑的估價值
if( X的估價值小於CLOSE表的估價值 )
把X從CLOSE列表中移除; 把X節點放入OPEN //取最小路徑的估價值
if(X not in both)
求X的估價值;
並將X插入OPEN表中; //還沒有排序
}
將n節點插入CLOSE表中;
按照估價值將OPEN表中的節點排序; //實際上是比較OPEN表內節點f的大小,從最小路徑的節點向下進行。
}
/*
通過幾天的學習啟發學習法搜尋以及解決相應的一個演算法題,對搜尋演算法有了進一步的認識.
感謝這些部落客人的翻譯:
http://blog.vckbase.com/panic/archive/2005/03/20/3778.aspx
http://blog.vckbase.com/panic/archive/2005/03/28/4144.html
http://www.5igis.com/algorithm/shortpath.htm
http://hi.baidu.com/sonwkuni/blog/item/8be877af32b069cb7dd92a30.html
*/
//我用了stl的優先隊列,而且,沒有實現去更新open表中的已有的值,我只是用一個新的節點加到open表中
//當然你可以運用自己寫的最小堆來維護這個值,有興趣的可以參考這裡:點擊開啟連結
#include <queue>#include <string>#include <algorithm>#include <cstdio>using namespace std;struct node{ string a;//name int d;//depth,and the g()'s value int z;//0's weizhi int p;//priority,the f()'s value friend bool operator < (const node & n1, const node & n2)//p值越小優先順序越高 { return n1.p > n2.p; } node():d(0),z(0),p(999999999){}};node temp;node now;string start("123456780");//初始化狀態 string end("123456780");//最後狀態 int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};int hash[362881][2];//hash表 :[][0] = 等於0說明還沒進過open表,大於0代表已經在open列表中了,而且其值就是f值 // [][1] = 大於0代表已經在close列表中了,而且其值就是f值 int dir[9][4] = { //0所在位置與交換後可以到達的位置,9表示不能移動到的位置 {1,3,9,9},//0 {0,2,4,9},//1 {1,5,9,9},//2 {0,4,6,9},//3 {1,3,5,7},//4 {2,4,8,9},//5 {3,7,9,9},//6 {4,6,8,9},//7 {5,7,9,9},//8 };int xy[9][2] = {//為每個位置設立座標 {1,1},//0 {1,2},//1 {1,3},//2 {2,1},//3 {2,2},//4 {2,3},//5 {3,1},//6 {3,2},//7 {3,3},//8 }; priority_queue<node> q;void init(){ for(int i = 0; i < 9; ++i) scanf("%s",&end[i]);}int hashindex()//排列與hash { int result = 0; string tt = temp.a; for(int j =1; j < 9; ++j)//從第二個數開始 { int count = 0;//逆序的個數 for(int k = 0;k < j; ++k) //如果前面的大於後面的則逆序個數+1 { if(tt[j] < tt[k]) ++count; } result += count*fac[j];//1-9的階層 } return result;}int h()//曼哈頓距離 { string a = end; string b = temp.a; int ans = 0; for(int i = 0; i < 9; ++i) { if(b[i] == '0') continue; for(int j = 0; j < 9; ++j) { if(b[i] == a[j]) { int xx = xy[i][0]-xy[j][0]; if(xx < 0) xx = -xx;//|x1-x2| int yy = xy[i][1]-xy[j][1]; if(yy < 0) yy = -yy;//|y1-y2| ans += (xx +yy);// |x1-x2|+|y1-y2| break; } } } return ans;}bool bfs(int zz,int xx)//0的位置,被交換的方位 { temp = now; temp.a[zz] = temp.a[xx]; temp.a[xx] = '0'; if(temp.a == end)//找到目標節點就返回true return true; int isok = hashindex(); temp.d += 1; temp.z = xx; temp.p = temp.d+h(); if(hash[isok][1] > 0 && hash[isok][1] > temp.p)//假如已經在close列表中了,而且現在的F值比原來的小 { //說明有更好的路徑,因為他們的h值都相同. hash[isok][1] = 0;//從close表中出去 q.push(temp); return false; } if(hash[isok][0] == 0 || hash[isok][0] > temp.p)//還沒在open表中,或者已經在open表中了但是現在的F值比原來的小 { hash[isok][0] = temp.p;//進入open表或者更新open表中原有的節點的值 q.push(temp); return false; }}int A_star(){ int ans = 0; temp.a = start; temp.d = 0; temp.z = 8; hash[hashindex()][0] = 0;//起始狀態 temp.p = 0; q.push(temp); while(!q.empty()) { now = q.top(); q.pop(); temp = now; int nowhashindex = hashindex(); if(hash[nowhashindex][1] > 0) continue;//如果已經在close表中了就跳過 for(int i = 0; i < 4; ++i) { if(dir[now.z][i] < 9 && bfs(now.z,dir[now.z][i]))//如果可以被擴充 { ans = now.d+1; return ans; } } hash[nowhashindex][1] = now.p;//進close表 }}int main(){ init(); printf("%d\n",A_star()); return 0;}