A* (Astar)

A* 启发式搜索

 引言:

先前提到过的优先队列算法,每次都取出当前价值最小的值进行扩展,虽然已经大大的降低了复杂度,但也会有很多数据来卡你的复杂度;

比如说上面这个图,我们会一直沿着右边这条路搜索,

然而最后我们发现一直沿着这条边搜索下去答案会变得很大,

这几次搜索就没什么用了;

最终答案为12;

在这样的基础上,我们可以对未来可能产生的情况进行预估;

设计了一个“估价函数”;

表示从该状态目标状态的估计的价值;

用f(x)来表示估价函数;

用g(x)表示实际上从该状态到目标状态的价值

astar 的算法核心就是从堆中不断取出 [f(x)+当前代价] 的最小值进行扩展;

定理:

对于每一个当前状态 x

 有f(x)<=g(x)恒成立;

若不成立,正确答案无法得出;

举个例子:

上图的数字代表着当前节点的f()值,也就是估计值,以及边权;

我们以更新最后一个答案来举一个例子:

红色代表着当前代价;

这时的

队列为: 3+7,4+6,5+10

取出:3+7;

得到12+0;插入队列;

此时队列为:4+6,12+0,5+10;

取出:4+6;

得到:12+0;

再次取出时,取出的值是12+0;

此时目标节点第一次被取出应为答案;

ans=12;

然而事实上这道题根据优先队列来做应该是 8

为最左端的一条路径;

由此可见当估价函数被错误地估计到了大于实际价值时,

我们的正解实际上是被压在堆里出不来;

根据设计估价函数小于实际代价:

假如某非最优解路径上的某一点s先被扩展;

那么,当目标节点被取出来前的某一时刻:

1、s为非最优解,所以s的当前代价大于从 start 到 end 的最小代价 minn;

2、状态 t 为最优解路径上的一个状态,就有 t状态代价+f(t) <= t当前代价+g(t) =minn;

所以s当前代价>t当前代价+f(t),t状态就会比s先扩展,从而找到最优解;

A* 算法的实质:

  带有估价的优先队列bfs;

第k短路

这个题目的大意是:给了一个n个点,m个边的图,求start 到 end的第k短路长度;

一个比较直观的做法是优先队列bfs

当一个状态被第一次取出时:我们可以得到从出态到此状态的最小代价;

推论:当一个状态被第i次取出时,对应的代价是start 到 此状态的第i短路;

所以当目标节点被第k次取出时,我们就得到了答案

我们用优先队列极有可能会超时,

所以我们使用astar 算法;

按照f()的设计准则,f(x)要小于等于实际的x到end的第k短距离;

所以我们可以设计f为当前节点x到end 的最短路

然后进行优先队列算法,当第k次取出时,即为答案;

怎样记录节点被取出呢?

我们可以用一个二元组pair<int,int> 。这时c++stl自带的一个组,可以把他想象成一个两个元素的结构体;

然后优先队列也用二元组定义;

因为我们加上了一个估价函数,我们的实际时间复杂度被大大降低,快速求出结果。

猜你喜欢

转载自www.cnblogs.com/lirh04/p/12890989.html