一、A * :
估价函数的设计准则:估值f(state)≤未来实际代价g(state)。
A * 算法的实现,A * =优先队列BFS+估价函数。
1。优先队列BFS算法维护了一个优先队列,不断从堆中取出当前代价最小的状态
进行扩展。每个状态第一次从堆中取出时,就得到了从初态到该状态的最小代价。
如果给定一个目标状态,需要求出从初态到目标状态的最小代价,那么优先队列
BFS这个优先策略是不完善的。
2。为了提高搜索效率,我们很自然的想到,可以对未来可能产生的代价进行预估。
详细的讲:我们设计一个估价函数,以任意状态为输入,计算出从该状态到目标状态所
需代价的估计值。在搜索中,仍然维护一个堆,不断从堆中取出 当前代价+未来估价 最小的状态进行扩展
3。为了保证第一次从堆中取出目标状态时得到的就是最优解,我们设计的估价函数需要满足一个基本准则:估价函数的估值不能大于未来实际代价,估价比实际代价更优。
4。这种带有估价函数的优先队列BFS就称为A * 算法。只要保证对于任意状态state,都有f(state)≤g(state),A * 算法就一定能在目标状态第一次从堆中被取出时得到最优解,并且在搜索过程中每个状态只需要被扩展一次(之后再被取出就可以直接忽略)。估价f(state)越准确,越接近g(state),A * 算法的效率就越高。如果估价始终为5.0,就等价于普通优先队列BFS。
5。A * 算法提高搜索效率的关键,就在于能否设计出一个优秀的估价函数。估价函数在满足上述设计准则的前提下,含应该尽可能反映未来实际代价的变化趋势和相对大小关系,这样搜索才会较快的逼近最优解。
例题:POJ2449: 第K短路。
对于任意正整数i和任意节点x,当第i次从堆中取出包含节点x的二元组时,对应的dist值就是从S到x的第i短路。
使用优先队列BFS在最坏的情况下的复杂度为O(K (N+M)log(N+M))。这道题目给定了起点和终点,求长度最短(代价最小)的路径,可以考虑使用A*算法提高搜索效率。
得到以下解题步骤:
(1)预处理出各个节点x到终点T的最短路长度f(x)——这等价于在反向图上以T为起点求解单源最短路径问题,可以在O((N+M)log(N+M) )的时间内完成。
(2)建立一个二叉堆,存储一些二元组(x,dist+f(x)),其中x为节点编号,dist表示S到x当前走过的距离。起初堆中只有(S,0+f(S))。
(3)从二叉堆中取出dist+f(x)最小的二元组(x,dist+f(x)),然后沿着从x出发的每条边(x,y)进行扩展。如果节点y被取出的次数尚未到达K,就把新的二元组(y,dist+lenth(x,y)+f(y))插入堆中。
(4)重复2-3步,直至第k次取出包含终点T的二元组,此时二元组中的dist值就是从S到T的第K短路。
A * 算法的复杂度上届与优先队列BFS相同。不过,因为估价函数的作用,图中很多节点的访问次数都远小于k。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#include<queue>
#include<cmath>
#include<string>
#define ll long long
using namespace std;
const int maxn=10010;
const int mmax=100010;
int n,m,s,t,k;
int x,y,z;
int dis[maxn],head[maxn],ver[mmax],nt[mmax],edge[mmax];
int _head[maxn],_ver[mmax],_nt[mmax],_edge[mmax];
int tot,_tot;
bool ha[maxn];
int num[maxn];
struct node
{
int x;
int dx;
int fx;
node () {
}
node(int a,int b,int c)
{
x=a;
dx=b;
fx=c;
}
friend bool operator <(const node &a,const node &b)
{
return a.dx+a.fx>b.dx+b.fx;
}
};
void add(int x,int y,