1. 程式人生 > 實用技巧 >【圖論】【演算法】A*演算法 - 第k短路

【圖論】【演算法】A*演算法 - 第k短路


用得不多,就不講那麼詳細了


功能實現

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\n",AStar(n,st,ed,k));
    
    return 0;
}

實在沒找到啥非常模板的模板題,略過——