題目
http://poj.org/problem?id=3255
題意:求從起點到終點的次短路。次短路是指比最短路長的路徑中,相對最短的那一條。
解題思路
《挑戰程序設計競賽》中的一道題。
引用別人的博文->圖論算法小結:次短路的求解
首先來回顧一下Dijkstra算法的原理:首先把所有結點的最短距離設置爲無窮大,然後令d[0]=0。接下來,每次都找到最短路已經確定的經典,更新從它出發的相鄰結點的最短距離。以後我們不再考慮最短距離已經確定了的結點。
以上就是Dijkstra算法的主要過程,需要注意的一點是我們不再考慮的是“最短距離已經確定了的結點”,不要錯誤地理解爲是更新過最短距離值的結點。因爲有些結點的最短路可能需要多次更新才能最終確定。那麼問題來了,如何知道哪些結點的最短距離是確定的呢?這裏我們利用了一點貪心的思想,每次都取出當前距離最短的那個結點,認爲它的最短路就是已經確定好的。可以證明這樣的做法是正確的。這也算爲什麼優化版本的DIjkstra算法用到了priority_queue的原因。
那麼回到主題,如何求解次短路呢?如果我們要求解起點s到終點t的次短路,那麼有兩種可能的情況:(1)起點s到某個頂點u的最短路+d(u,t)。(2)起點到某個頂點u的次短路+d(u,t)。因此,對於每個結點,我們記錄的不僅僅是最短距離,還有次短距離,接下來用類似於Dijkstra算法不斷更新這兩個距離即可求出次短路了。
解題思路
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
struct edge
{
int to, cost;
edge(){}
edge(int _to, int _cost) //構造邊的終點和權重
{
to = _to;
cost = _cost;
}
};
typedef pair<int, int> P;
const int maxn = 5005, INF = 1<<27;
int N, R;
vector<edge> G[maxn]; //G[0...n-1],G[i]存放從i出發的所有邊
int dist[maxn], dist2[maxn]; //dist存放最短距離,dist2存放次短路距離
void init() //初始化
{
fill(dist, dist + maxn, INF);
fill(dist2, dist2+ maxn, INF);
}
void dijkstra(int src) //dijkstra算法變形——兼顧最短路和次短路
{
init();
priority_queue<P, vector<P>, greater<P> > q;
dist[0] = 0; //標記源點的最短路爲0,但不標記次短路
q.push(P(0,src));
while(!q.empty())
{
P out = q.top(); q.pop();
int pos = out.second, d = out.first;
if (d > dist2[pos]) continue; //取出的比當前次短距離還長,也就比最短距離更長,跳過
//鬆弛,從pos出發的每條邊鬆弛
for (int i = 0; i < G[pos].size(); ++i)
{
edge &e = G[pos][i]; //pos到某個頂點的邊
int d2 = d + e.cost; //源點通過pos到e.to的新距離
if (d2 < dist[e.to]) //d2能直接更新最短路
{
swap(dist[e.to], d2); //替換下最短路
q.push(P(dist[e.to], e.to));
}
//d2的可能性:1.不能替換最短路,但是可以直接替換次短路;2.之前最短路被替換下來的值可以更新次短路
if (d2 > dist[e.to] && d2 < dist2[e.to])
{
dist2[e.to] = d2;
q.push(P(dist2[e.to], e.to));
}
}
}
}
int main()
{
cin >> N >> R;
int c1, c2, cos;
for (int i = 0; i < R; ++i) //輸入每條路徑
{
cin >> c1 >> c2 >> cos;
G[c1-1].push_back(edge(c2-1, cos));
G[c2-1].push_back(edge(c1-1, cos));
}
dijkstra(0);
cout << dist2[N-1] << endl;
return 0;
}