【筆記】Dijkstra algorithm 單點源最短路徑

  • 【用途】給定一張有向圖,找出起點與終點(或其它頂點)之間的最短路徑。
  • 【條件】沒有負權重的邊。
  • 【原理】Greedy + DP
    • 如果存在一條邊 【s –> t】,權重為 w,則從【起點 –> t】的最短路徑可以透過【起點 –> s】+ 【s –> t】 來拓展。
    • 每次都利用已經確定最短距離的頂點來拓展到其相鄰的頂點。
    • 如果dis[s] + w 比 dis[t] 小,則更新【起點 –> t】的距離成 dis[t] = dis[s] + w。
    • 持續拓展每條邊,直到所有的 dis[x] 都代表【起點 –> x】的最短距離。
    • 每條邊只會被拓展一次。
  • 【實作】
    • vector<pair<int, int> >:存圖。first是「下一個頂點」的編號,second是連結「下一個頂點」的邊的權重,。
    • 陣列dis[ ]:用來更新起點到各頂點的最短距離。
    • #define P pair<long long, int>
    • 【方法1】priority_queue<P, vector<P>, greater<P> > 儲存尚未拓展的頂點,first是起點到「鄰接頂點」的最短距離,second是「鄰接頂點」的編號。使用greater來對first進行由小到大的排序。
    • 【方法2】set<P> 儲存尚未拓展的頂點,first是起點到「鄰接頂點」的最短距離,second是「鄰接頂點」的編號。 (st.erase可縮減查找的時間。)
    • Line-19:避免走訪已經處理過的頂點。
  • 【時間複雜度】O( E * log (V) ),其中V為圖的頂點個數,E是邊數。
  • 【範例】UVa 10986 Sending email

【方法1】priority_queue

// Dijkstra - priority_queue
memset(dist, 0x3F, sizeof(dist));
priority_queue<P, vector<P>, greater<P> > pq;
dist[S] = 0;
pq.push({0, S});
while (!pq.empty()){
    P p = pq.top();
    pq.pop();
    if (dist[p.S] < p.F) continue;
    for (auto nxt: G[p.S]){
        if (dist[nxt.F] > dist[p.S] + nxt.S){
            dist[nxt.F] = dist[p.S] + nxt.S;
            pq.push({dist[nxt.F], nxt.F});
        }
    }
}

【方法2】set

// Dijkstra - set
memset(dist, 0x3F, sizeof(dist));
set<P> st;
dist[S] = 0;
st.insert({0, S});
while (!st.empty()){
    P p = *st.begin();
    st.erase(p);
    if (dist[p.S] < p.F) continue;
    for (auto nxt : G[p.S]){
        long long new_dist = p.F + nxt.S;
        if (new_dist < dist[nxt.F]){
            //st.erase({dist[nxt.F], nxt.F});
            dist[nxt.F] = new_dist;
            st.insert({dist[nxt.F], nxt.F});
        }
    }
}
分享本文 Share with friends