知识点-A*搜索和IDA *
解决问题
最短路径搜索,第k短路
概要
一般我们求最短路径都是用Dijkstra算法,这个算法可以比较快的找到一条最短最短路,但要是看这个算法的搜索范围,可以发现,这个算法对很多不需要的区域也进行了搜索,这就大大降低了它的速率。
Dijkstra算法只考虑到每个点的代价,那么我们改为考虑到每个点到终点的预计代价,会怎么样呢?这样的话,路径搜索就只会朝着靠近终点的方向进行扩展,要搜索的点数就大大下降了。但是由于我们只能对代价进行粗略的估计,所以可能会遇到下面这种情形:对于一些U型的障碍物,我们很容易察觉不到不能进入U型口,这样找到的路径就会是先进入U型口,再从里面出来,这样就不是最短路径了。
那么该怎么办呢?解决方法就是接下来要说的A*算法了,这个算法说白了就是将上述两个算法综合了一下,同时考虑每个点到终点的预计代价和到这个点的实际代价,从而吸收了这两个算法的优点,可以搜索较小范围就找到最短路径。
A*设定了两个函数,g(n)表示从初始结点到任意结点n的代价,h(n)表示从结点n到目标点的启发式评估代价,当从初始点向目标点移动时,A权衡这两者。每次进行主循环时,它检查f(n)最小的结点n,其中f(n) = g(n) + h(n)。
其中h(n)函数的设定可以控制A*算法的行为:
h(n)是0的时候,A*算法变为Dijkstra算法,保证可以找到最短路径
大部分的h(n)比实际代价小或等于的时候,A*保证可以找到最短路径,而且h(n)越小,要搜索的节点越多,速度越慢
h(n)很精确的等于实际代价时,A*只会搜索最短路径上的点,而不会对其他点进行扩展,当然这种情况一般是不现实的,因为我们很难对价值进行精确的估计
当h(n)经常性的大于实际代价时,A*算法将无法保证搜到的是最短路径,但它会运行的更快
所以在使用A*算法时,如何选取一个适合的h(n)函数就十分重要
下面记录几种在网格图中常用的h(n)函数:曼哈顿距离、对角线距离(可以在网格中走对角线),欧几里得距离
一些优化:
当存在好几条价值相同的路径时,可能会导致算法的效率降低,可以对h(n)函数添加一个附加值
附加值有很多种方法,目标是使相同的f值被区分开来
1、稍稍改变h的单位,让h*=(1.0+p),其中p选择为一个小于移动一步(step)的最小代价 / 期望的最长 路径长度的值
2、添加一个比较函数,在f值相等的时候,再添加附加值进行比较
3、添加一个附加值,让搜索倾向于从初始点到目标点的连线方向
d x 1 = c u r r e n t . x − g o a l . x ; dx1 = current.x - goal.x; dx1=current.x−goal.x;
d y 1 = c u r r e n t . y − g o a l . y ; dy1 = current.y - goal.y; dy1=current.y−goal.y;
d x 2 = s t a r t . x − g o a l . x ; dx2 = start.x - goal.x; dx2=start.x−goal.x;
d y 2 = s t a r t . y − g o a l . y ; dy2 = start.y - goal.y; dy2=start.y−goal.y;
c r o s s = a b s ( d x 1 ∗ d y 2 − d x 2 ∗ d y 1 ) ; cross = abs(dx1*dy2 - dx2*dy1); cross=abs(dx1∗dy2−dx2∗dy1);
h e u r i s t i c + = c r o s s ∗ 0.001 heuristic += cross*0.001 heuristic+=cross∗0.001
求k短路的时候,就是用A搜索从起点到终点的最短路径,当终点第k次被搜到的时候,对应的路径长度就是第k短路,为了加快速度,我们可以在搜索前先把终点到每个点的最短距离求出来作为h函数。
以上内容很多参考了《堪称最好最全的A算法详解》这篇博客,大家感兴趣的可以去搜一下
而IDA* 的话,就像A* 是BFS的优化一样,IDA*就是对迭代加深的优化,核心仍然是估价函数的计算,所以我把它一起写在这了。它和迭代加深的区别就是原来是深度大于期望深度就退出,变为深度加上估计还需深度大于期望深度就退出
复杂度
? ? ?
例题
[BZOJ 1085]、[Poj2449]
代码(K短路)
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define INF 0x7f7f7f7f
struct k_th
{
struct Node
{
int x,f,h;
Node(){}
Node(int a,int b,int c) : x(a),f(b),h(c){}
bool operator < (const Node& rhs) const
{
if(h==rhs.h)return f>rhs.f;
return h>rhs.h;
}
};
struct Edge
{
int to,nxt,w;
Edge(){}
Edge(int a,int b,int c):to(a),nxt(b),w(c){}
};
Edge edges[MAXN],redges[MAXN];
int n,cnte,rcnte,k;
int dis[1010],vis[1010],rhead[1010],head[1010];
void init(int inn,int ink)
{
this->n=inn;this->k=ink+1;cnte=rcnte=0;
memset(head,0,sizeof(head));
}
void add_edge(int a,int b,int c)
{
edges[++cnte]=Edge(b,head[a],c);head[a]=cnte;
redges[++rcnte]=Edge(a,rhead[b],c);rhead[b]=rcnte;
}
queue<int> q;
void spfa(int st)
{
while(!q.empty())q.pop();
memset(vis,0,sizeof(vis));
memset(dis,INF,sizeof(dis));
q.push(st);
dis[st]=0;vis[st]=1;
while(!q.empty())
{
int x=q.front();q.pop();vis[x]=0;
for(int i=rhead[x];i;i=redges[i].nxt)
{
if(dis[redges[i].to]>dis[x]+redges[i].w)
{
dis[redges[i].to]=dis[x]+redges[i].w;
if(!vis[redges[i].to])
{
vis[redges[i].to]=1;
q.push(redges[i].to);
}
}
}
}
}
priority_queue<Node> qq;
int Astar(int st,int ed)
{
spfa(ed);
memset(vis,0,sizeof(vis));
if(dis[st]==INF)return -1;
if(st==ed)k++;
qq.push(Node(st,0,dis[st]));
while(!qq.empty())
{
Node x=qq.top();qq.pop();
++vis[x.x];
if(vis[x.x]>k)continue;
if(vis[ed]==k)return x.f;
for( int i=head[x.x];i;i=edges[i].nxt )
{
int t1=x.f+edges[i].w;
int t2=t1+dis[edges[i].to];
qq.push( Node(edges[i].to,t1,t2) );
}
}
return -1;
}
}e;