A*搜索是最佳优先搜索最广为人知的形式,是一种有信息搜索策略,它的核心是一个估值函数:f(n)=g(n)+h(n),g(n)是从起始点到节点n的路径耗散,而h(n)是从节点n到目标节点的最低耗散路径的估计耗散值,因此f(n)=经过节点n的最低耗散解的估计耗散。
完备性证明:A*搜索能够找到最低耗散解的依据是一个可采纳启发式:h(n)不会高估经过节点n的实际耗散,采用可采纳启发式h(n)如果有个非最优目标节点G率先被搜索到,此时f(G) = g(G) + h(G) = g(G) > 最低耗散C*,而此时一定存在最低耗散路径上的节点n,f(n) = g(n) + h(n) <= C*,因此G不会被采纳,搜索始终会终止于一个最优解。如果一个启发式满足一致性,也就是:如果对于每个节点n和通过任何行动a生成的n的每个后继节点n',从节点n到达目标的估计号耗散值不大于从n到n'的但不耗散与从n'到达目标的耗散值之和,即h(n) <= c(n,a,n') + h(n'),类似于三角形不等式,很容易证明满足一致性的启发式就是可采纳启发式。
伪代码:
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="background-color: rgb(204, 204, 204);"><span style="font-size:12px;">open ← {s};
closed ← Ø
while open != Ø do
remove leftmost state from open, call it x
if x is a goal then return success
else
generate children of x
for each child c do
if c is already on open list then
if c was reached by a shorter path then
update f(c)
else if c is already on closed list then
if c was reached by a shorter path then
remove c from closed list
add c to open list and update f(c)
else
assign c a heuristic value f(c)
add c to open list
put x on closed list and reorder states on open by f
return failure</span></span>
</span>
曾经写过的HDU1043八数码问题用了A*搜索:
<span style="font-family:Microsoft YaHei;font-size:14px;">
<span style="font-size:12px;">#include<iostream>
#include<queue>
#include<stack>
#include<memory.h>
#include<math.h>
#include<algorithm>
#define INF 400000
#define END 322560
using namespace std;
struct NODE
{
int board[3][3];
int x,y;
int h,g;
int state;
bool operator < (const NODE & node) const
{
return (h + g) > (node.h + node.g);
}
}sta;
int dir1[4][2] = {{0,-1},{0,1},{-1,0},{1,0}};
char dir2[4] = {'l','r','u','d'};
int fact[9] = {1,1,2,6,24,120,720,5040,40320};
int vis[INF],pre[INF];
void init(char *);
int get_hash(const NODE &);
void aStar();
void print();
int check(const NODE &);
int val(const NODE &);
int main()
{
char str[100];
// freopen("Sample Input.txt","r",stdin);
while(cin.getline(str,100))
{
init(str);
if(!check(sta))
{
cout << "unsolvable" << endl;
}
else
{
aStar();
print();
}
}
return 0;
}
void init(char str[])
{
int pos = 0;
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 3;j++)
{
int pos = (3 * i + j) * 2;
if(str[pos] == 'x')
{
sta.board[i][j] = 0;
sta.x = i;
sta.y = j;
}
else
{
sta.board[i][j] = str[pos] - '0';
}
}
}
sta.g = 0;
sta.h = val(sta);
sta.state = get_hash(sta);
memset(vis,-1,sizeof(vis));
memset(pre,-1,sizeof(pre));
vis[sta.state] = 1;
}
int get_hash(const NODE & node) //用康托展开获得此时排列在所有排列中的位置
{
int num[9];
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 3;j++)
{
num[3 * i + j] = node.board[i][j];
}
}
int res = 0;
for(int i = 0;i < 9;i++)
{
int cnt = 0;
for(int j = 0;j < i;j++)
{
if(num[j] > num[i])
{
cnt++;
}
}
res += cnt * fact[i];
}
return res;
}
void aStar()
{
priority_queue<NODE> que;
que.push(sta);
while(!que.empty())
{
NODE cur = que.top();
que.pop();
if(cur.state == END)
{
return;
}
for(int i = 0;i < 4;i++)
{
NODE next = cur;
next.x += dir1[i][0];
next.y += dir1[i][1];
if(next.x < 0 || next.y < 0 || next.x > 2 || next.y > 2)
{
continue;
}
swap(next.board[cur.x][cur.y],next.board[next.x][next.y]);
next.state = get_hash(next);
if(vis[next.state] < 0)
{
vis[next.state] = i;
pre[next.state] = cur.state;
next.g++;
next.h = val(next);
que.push(next);
}
}
}
}
void print()
{
int pos = END;
stack<char> path;
while(pre[pos] != -1)
{
path.push(dir2[vis[pos]]);
pos = pre[pos];
}
while(!path.empty())
{
cout << path.top();
path.pop();
}
cout << endl;
}
int check(const NODE & node) //剪枝
{
int num[9];
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 3;j++)
{
num[3 * i + j] = node.board[i][j];
}
}
int cnt = 0;
for(int i = 0;i < 9;i++)
{
if(num[i] == 0)
{
continue;
}
for(int j = 0;j < i;j++)
{
if(num[j] != 0 && num[j] > num[i])
{
cnt++;
}
}
}
if(cnt % 2 == 0)
return 1;
else
return 0;
}
int val(const NODE & node) //没有X时的曼哈顿距离,很明显符合一致性
{
int sum = 0;
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 3;j++)
{
if(node.board[i][j])
{
sum += abs((node.board[i][j] - 1) / 3 - i) + abs((node.board[i][j] - 1) % 3 - j);
}
}
}
return sum;
}</span>
</span>