这东西,简直是被神化了一样。我从蓝书上查找了一些资料,以我的水平确实难以看懂他的表达。于是借助百度,发现大多数博客都在转载一篇似乎是翻译自国外的文章。写的很详细,但我在第一次阅读的时候并没有看懂,并且感觉专业性较高。比如其中又是OpenList又是CloseList的,初看确实有些头晕眼花。
那么,在这里重新整理一下我理解A*的过程。
概念理论
这个算法似乎被广泛应用于游戏开发的寻路算法里。他是启发式搜索的算法。相比于传统的搜索,你可以理解为他是边搜边看和终点的偏差的,也就是他自己在尽可能修正自己的路线的感觉。
那么怎么实现这个“让程序自我认识并修正路线”呢?
我们引入一个经典的成语故事。
话说,南辕北辙…xxxxxxxx
是的,如果一个搜索算法的路径如同南辕北辙一样,其效率一定是极其低下的。那么在选择移动的时候,我们每次都要尽可能的去选一个靠近终点的移动。那么如何让程序知道某个点更靠近于终点呢?
这里就要引入一个估价的概念。
估价函数,它所实现的功能即为:以任意状态的输入,计算出该状态到目标状态产生代价的估计值。
通过这个估价,我们让程序实现两个考虑点。“考虑中间某个点与初始点的距离”,“考虑中间某个点与终点的距离”。当两者之和最小时,最短路径也就自然产生。
听完这些口胡(口头胡扯233),就可以去看一下大佬的文章了。
还能复习一波深广搜,多棒唷233
看完这个可能虽然有了感觉,但还是觉得有些东西没被点明白。没关系,这个时候去进攻那个国外的文章,就轻松加愉快了。
这里面包含了A*算法的模拟过程,相当详细,大佬很强啊!
小做整理
由于转载进来的东西太多了…所以简单梳理一下。
首先我们要维护几个东西:
Open List:需要不断去处理的“受关注”的点列表
Close LIst:已经“不需要受到关注”的点列表
可能需要一个二叉堆,便于之后的“总是取出F最小值”的操作。
完成一个功能:
预估路径->这个功能可以求出的内容:
F:最终预估值,有F=G+H
G:从起点 A 移动到指定方格的移动代价
H:从指定的方格移动到终点 B 的估算成本
结束时的状态:
目标点被加入OpenList。
或
查找终点失败,并且 open list 是空的,此时没有路径。
如何去维护两个List?/两个List的关系是什么?
在初始的时候,我们将起点加入OpenList。
在每次进行搜索的时候,都会取OpenList中F值最小的点做处理,然后将其移入CloseList。
对最F最小点做了什么处理?
对于被取出的F值最小的点,对其周边点进行如下处理(A、B、C中只择其一):
A.当该周边点是不可达的(即该点为障碍物,不可被作为路径点)或存在于CloseList中时:
忽略,不进行任何处理。
B.当该周边点尚未处于OpenList中时:
把它加入 Open List ,并且把当前方格设置为它的父亲(如果你需要具体路径的话),记录该方格的 F , G 和 H 值。
C.该周边点已经位于OpenList中时:
我们假设该节点的父节点被重载为F值最小点。用因为这个假设而产生的新的G值来与以起点为父节点产生的旧的G值做对比。如果新G值更小,那么更换该周边点的父节点,并为其重新计算F、G、H。
如何去实现预估功能?
这里仅仅介绍一种。
这个距离算法,忽略障碍物,不斜着走。就是一道横线一道竖线通往终点。计算公式:d(i,j)=|xi-xj|+|yi-yj|
一般用途
k短路
为了找代码,我在网上游荡了很久。发觉更多的都是被融在求第k短路径。虽然不知道为什么,但为了能方便呈现出A*的板子代码,这里决定先行分析现有资源
什么是k短路问题?
在研究学习过程中,发现这个东西是涉及到几个比较需要动脑子的知识点的。
因为这里涉及一个预处理路径的问题,所以我发现网上有人用SPFA,有人用dijkstra。那么各取所好吧。反正我觉得Dj用的频率好像多一点…
题目中的Show Time
魔法猪学院 SDOI2010
据说LuoGu加强了数据,导致A*会有一个点被卡掉。但这个对我们不产生影响,因为我们只是想拿这道题来熟悉一下A*的代码实现而已。
----7.23补档
关于A*,我已经研究了整整三天了。今天我终于悟到了这道题的写法(虽然代码依然不是我自己写的2333),但我前前后后写了有四个板子去试去撞。撞了一排又一排的WonderfulAnswer。然后今天的模拟赛我还又打了一手A*试了试,难得写对了跑掉了样例。(然而那道题用A*的话时间爆炸…一分都拿不到)。
诉苦结束。说几个注意事项:
跑Spfa的时候,是用反图跑的。你需要把边反转过来,而不是用正边跑。否则会得到一个满满的除了初始点是0其他都是初始化的距离数组。
这道题不是经典无脑模板题,依然需要你手动去改一点东西。
下面的代码有一些小不一样。在这里,我们用Spfa预处理的就是该点到终点的距离H。然而因为这代码不是我写的,所以这代码里的h是指代节点与出发点的距离。大家做一下区分。
#include<bits/stdc++.h>
using namespace std;
int g,n,m,head[5005],tail[5005],ans,bj[200005];
double ch[5005],e;
struct node{
int to,n;
double w;
}pr[200005],rpr[200005];
struct data{
int d;
double h,f;
bool operator < (const data &a) const {
return f>a.f;
}
}zz,mm;
priority_queue <data> q;
queue <int> p;
void add(int u,int v,double w){
pr[++g].w=w;
pr[g].to=v;
pr[g].n=head[u];
head[u]=g;
rpr[g].w=w;
rpr[g].to=u;
rpr[g].n=tail[v];
tail[v]=g;
}
void spfa()
{
memset(ch,0x7f,sizeof(ch));
bj[n]=1;
ch[n]=0;
p.push(n);
while(!p.empty())
{
int u=p.front();
p.pop();
bj[u]=0;
for(int i=tail[u];i;i=rpr[i].n)
{
int v=rpr[i].to;
double z=rpr[i].w;
if(ch[u]+z<ch[v])
{
ch[v]=ch[u]+z;
if(!bj[v])bj[v]=1,p.push(v);
}
}
}
}
void A(){
zz.d=1,zz.h=0,zz.f=0;
q.push(zz);
while(!q.empty())
{
zz=q.top();q.pop();
if(zz.f>e)return;
int u=zz.d;
if(u==n)
{
e-=zz.f;ans++;continue;
}
for(int i=head[u];i;i=pr[i].n)
{
mm.d=pr[i].to;
mm.h=zz.h+pr[i].w;
mm.f=mm.h+ch[pr[i].to];
q.push(mm);
}
}
}
int main()
{
cin>>n>>m;
scanf("%lf",&e);
for(int i=1;i<=m;i++)
{
int x,y;cin>>x>>y;
double z;scanf("%lf",&z);
add(x,y,z);
}
spfa();
A();
printf("%d",ans);
return 0;
}