浅谈A*
Posted nevereasy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈A*相关的知识,希望对你有一定的参考价值。
本文章只表达本人对A*的一些见解和看法,如果有写错的地方,或者你有不同的观点你来打我啊,欢迎指出
A*看上去很难,而网上有些文章里的代码也真的很长很难理解
但是
那些很长很长、定义一个头文件(关于C++的文章)、用很多看都没看过的函数的文章
大多是关于工程设计(游戏什么的),与我们OI没有很大的关系
A*算法就是结合 Dijkstra算法和 的最佳优先搜索一个神奇的算法
最佳优先搜索好像不常见
如果不会这两个的朋友可以先去学习一下
简单点的 A*就是 优先队列+估价函数
网上很多文章都提到了A*算法最重要的是启发式函数(好像就是估价函数)
F=G+H
F 决定下一步走的位置
G 表示从起点到指定位置的距离
H 表示从指定位置到终点的预计距离
接下来分析几种情况:
当H(n)=0时,那么算法会退化成Dijkstra,虽然还是能找到最短路,但是会变慢
当H(n)比G(n)大许多时,则算法会退化成BFS(最佳优先搜索)不是宽度优先搜索!!!
当H(n)越小时,搜索到的节点就越多,搜索所用的时间也越长,但是能保证找到一条最短的路径
而H(n)越大时,搜索到的节点就越少,搜索所用的时间也会随之减少,但是不能保证找到最短的路劲
所以运用的时候一定要注意了
对启发式函数讲得详细的文章贴在本文末尾了才不是因为语文不好讲不清楚
A*的具体做法:
1.将起点S存入开放列表//开放列表:储存结点的优先队列或堆
2.将与起点S连接的结点放入开放列表,将起点设为其他结点的父结点
3.将起点S从开放列表中取出,放入关闭列表//关闭列表:储存不需要继续检查的结点的数组(这里就没必要用优先队列了)有时候没有也没关系
4.选择一个开放列表中F值最小的结点B,将B从开放列表中取出,放入关闭列表其实只要取出就可以了,关闭列表无所谓的
5.将与B相连的且未在开放列表中的结点放入开放列表
6.如果到达终点就退出,否则返回4
看到这里是不是感觉很迷?
实际上 A*算法往往都是与其他算法一起运用
关于A*的题目,比较经典的就是K短路问题了(因为我只看到K短路,其实一般的最短路也能做)
dalao说A*可以做很多搜索的,但那样就直接退化成了DIjkstra
在k短路问题中
我们可以先用dijkstra或者spfa来求出每个点的H值
接着
来我们做一道题目,加深理解(更迷)~~
然而某谷上只有一个K短路问题,而且还卡A*,要加一个很长的特判才能过
所以我们用POJ上的题目Remmarguts‘ Date
题目大意:
让你在有n个点m条边的有向图
找到s结点到t结点的第k短路,如果找不到就输出-1
有多组数据
输入数据:
第1行两个数n,m
第2~m+1行,每行3个数u,v,w,表示结点u到结点v有一条距离为w的结点
第m+2行,3个数s,t,k,表示求结点s到结点t的第k短路的距离
输出数据
一行,结点s到结点t的k短路的距离
样例输入
2 2
1 2 5
2 1 4
1 2 2
样例输出
14
图画得好丑
从图中可以看出,
1到2的最短路是 1->2 距离为5
第2短路是 1->2->1->2 距离为5+4+5=14
所以答案是14
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
#define re register
#define isnum(x) (x<58&&x>47)
const int maxn=100001;
const int INF=2147383647;
struct edge{
int to,nxt,w;
}z[maxn<<1],f[maxn<<1];
struct node{
int to,g,f;
bool operator <(const node& x) const {
if(!(x.f^f))return g<x.g;
return f>x.f;
}
};
int n,m,cnt,tot;
int s,t,k;
int head1[maxn<<1],head2[maxn<<1],dis[maxn<<1],vis[maxn<<1];
inline void add(int u,int v,int w){
cnt++;
z[cnt].to=v;
z[cnt].nxt=head1[u];
z[cnt].w=w;
head1[u]=cnt;
f[cnt].to=u;//反向建边
f[cnt].nxt=head2[v];
f[cnt].w=w;
head2[v]=cnt;
}
inline void dijkstra(int start){
for(re int i=1;i<=m;i++)
dis[i]=INF;
dis[start]=0;
vis[start]=1;
queue<int>q;
q.push(start);
while(!q.empty()){
int v=q.front();
q.pop();
int d=dis[v];
vis[v]=0;
for(re int i=head2[v];i!=-1;i=f[i].nxt){
int u=f[i].to,d=f[i].w;
if(dis[u]>dis[v]+d){
dis[u]=dis[v]+d;
if(!vis[u]){
q.push(u);
vis[u]=1;
}
}
}
}
}
inline int A_star(){
if(!(dis[s]^INF)){
return -1;
}
if(!(s^t))k++;
priority_queue<node>q;
node y;
y.to=s;y.g=0;y.f=dis[s];
q.push(y);
while(!q.empty()){
node x=q.top();
//printf("%d %d %d %d
",x.f,x.g,x.to,t);
//getchar();
q.pop();
if(!(x.to^t)){
tot++;
if(!(tot^k)){
return x.g;
}
}
for(re int i=head1[x.to];i!=-1;i=z[i].nxt){
int v=z[i].to,g=x.g+z[i].w,f=g+dis[v];
y.f=f;y.g=g;y.to=v;
q.push(y);
}
}
return -1;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
tot=0;
cnt=0;
memset(z,0,sizeof(z));
memset(f,0,sizeof(f));
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
memset(vis,0,sizeof(vis));
for(re int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
scanf("%d%d%d",&s,&t,&k);
dijkstra(t);
printf("%d
",A_star());
}
return 0;
}
dalao不告诉我怎么让代码背景透明
最后推荐两篇写得好的关于A*文章,本人也是看了之后才会的(如果你看了我讲的之后还不懂,就去看看):
推荐看这篇文章的思路
这篇文章真的特别详细,但是因为是译文,有些作者不确定的部分没翻译
以上是关于浅谈A*的主要内容,如果未能解决你的问题,请参考以下文章