主要搜索过程:
创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。
遍历当前节点的各个节点,将n节点放入CLOSE中,取n节点的子节点X,->算X的估价值->
{
从OPEN表中取估价值f最小的节点n;
if(n节点 in CLOSE) continue;
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表中; //还没有排序
}
按照估价值将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;
}