【图论】A*算法


用得不多,就不讲那么详细了


功能实现

A*算法最主要的部分就是它的估价函数(f(i)=g(i)+h(i))

(g(i))为到达某点已经付出的代价,(h(i))为该点到终点的估计代价

则估价函数则为两者之和

放入优先队列中,将会先处理估计总代价最小的状态,以取得(k)短路


求从点(st)到点(ed)的第(k)短路的长度

如上面估价函数所需要的,我们需要先求出(h(i)),即预处理出图中每个点到终点(ed)的估计代价(最短路径)

对于多起点同终点类型问题,可以将有向图中所有边全部取反,获得一张反向图,然后以(ed)为起点跑遍整张反向图求出最短路即可

所以此时只需要跑一边SPFA求出(ed)到任意点的最短距离(dis[i]),即可当作正向图中的(h(i))

然后便是Astar的主要部分

建立一个新结构体(node)保存搜索过程中每一种状态({to,g(i),f(i)}),根据上述估价函数,重载运算符时将(f(i))作为主关键词,将(g(i))作为次关键词

从小到大排序(放入小顶堆优先队列中),或从大到小排序(放入大顶堆优先队列中)

以类似bfs的方法,从起点(st)开始搜索,以优先队列作为搜索队列

每当搜索到一次(ed)点,就表明找到了一条最短路,直到第(k)次搜索到(ed)点时,便可以直接返回当前状态的(g(i))作为第(k)短路的代价

若在队列为空前均没有返回,说明不存在第(k)短路



代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;

const int INF=0x3f3f3f3f;
const int maxn=100050;

struct node
{
    int to,g,f;
    bool operator < (const node &r) const
    {
        if(r.f==f)
            return r.g<g;
        return r.f<f;
    } //从大到小排序,放入大顶堆内(使用时就会变成从小到大)
};
vector<P> G[maxn]; //正向图,{first,second}={to,cost}
vector<P> IG[maxn]; //反向图
bool inq[maxn];
int dis[maxn]; //dis[i]为i到ed的最短路径

void init(int n)
{
    for(int i=1;i<=n;i++)
    {
        G[i].clear();
        IG[i].clear();
    }
}

void SPFA(int n,int st)
{
    queue<int> que;
    for(int i=1;i<=n;i++)
    {
        dis[i]=INF;
        inq[i]=false;
    }
    dis[st]=0;
    inq[st]=true;
    que.push(st);
    while(!que.empty())
    {
        int cur=que.front();
        que.pop();
        inq[cur]=false;
        for(P &pd:IG[cur])
        {
            if(dis[pd.first]>dis[cur]+pd.second)
            {
                dis[pd.first]=dis[cur]+pd.second;
                if(!inq[pd.first])
                {
                    inq[pd.first]=true;
                    que.push(pd.first);
                }
            }
        }
    }
}

int AStar(int n,int st,int ed,int k)
{
    priority_queue<node> pq;
    if(st==ed)
        k++;
    node tmp=node{st,0,dis[st]};
    int cnt=0;
    pq.push(tmp);
    while(!pq.empty())
    {
        node nd=pq.top();
        pq.pop();
        if(nd.to==ed)
            cnt++; //找到一条最短路
        if(cnt==k)
            return nd.g; //当前找到的是k短路
        for(P &pd:G[nd.to])
        {
            tmp.to=pd.first; //去往的节点
            tmp.g=nd.g+pd.second; //当前代价g
            tmp.f=tmp.g+dis[pd.first]; //估计代价f=g+h
            pq.push(tmp);
        }
    }
    return -1;
}

int main()
{
    int n,m,u,v,c,st,ed,k;
    scanf("%d%d",&n,&m);
    init(n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&c);
        G[u].push_back(P(v,c));
        IG[v].push_back(P(u,c));
    }
    scanf("%d%d%d",&st,&ed,&k);
    SPFA(n,ed);
    printf("%d
",AStar(n,st,ed,k));
    
    return 0;
}

实在没找到啥非常模板的模板题,略过——


原文地址:https://www.cnblogs.com/stelayuri/p/13365068.html