题目
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;
}