题目链接:http://poj.org/problem?id=3255
Roadblocks
Description
Bessie has moved to a small farm and sometimes enjoys returning to visit one of her best friends. She does not want to get to her old home too quickly, because she likes the scenery along the way. She has decided to take the second-shortest rather than the shortest path. She knows there must be some second-shortest path.
The countryside consists of R (1 ≤ R ≤ 100,000) bidirectional roads, each linking two of the N (1 ≤ N ≤ 5000) intersections, conveniently numbered 1..N. Bessie starts at intersection 1, and her friend (the destination) is at intersection N.
The second-shortest path may share roads with any of the shortest paths, and it may backtrack i.e., use the same road or intersection more than once. The second-shortest path is the shortest path whose length is longer than the shortest path(s) (i.e., if two or more shortest paths exist, the second-shortest path is the one whose length is longer than those but no longer than any other path).
Input
Lines 2.. R+1: Each line contains three space-separated integers: A, B, and D that describe a road that connects intersections A and B and has length D (1 ≤ D ≤ 5000)
Output
Sample Input
4 4 1 2 100 2 4 200 2 3 250 3 4 100
Sample Output
450
Hint
Two routes: 1 -> 2 -> 4 (length 100+200=300) and 1 -> 2 -> 3 -> 4 (length 100+250+100=450)
找最短路的方法主要是Dijkstra。找次短路的方法平常用的不多。
这里提供一种比较暴力的算法,复杂度是O((2*|E|*log|v|)*|E|)
思路比较好理解:首先分析下次短路的特点,次短路是比最短路大,但是比其它任何路径都要短的路径,
那么次短路一定是最短路中的某一段路径(注意这里不是说最短路上的一条边,而是指最短路上某两个顶点之间的路径发生了变化)变长了,这个应该比较好理解,也比较好证明。这里就不赘述了。既然只有一段发生了变化,那么就可以枚举所有边,找到发生了变化的那条边。怎么又扯到边了。。。。下面解释下是什么意思。
因为题目固定了起点是第一个点,终点是第n个点。所以我们可以先求出起点到所有点的最短路lowCost0,已经终点到所有点的最短路lowCostn,假设发生变化的路径发生在点x 到 点y这段。那么最短路就可能是lowCost0[x] + e(x,y)+lowCostn[y]。
也就是起点到x的最短路 + x到y的距离 +y到终点的最短路,这样就有可能组成了最短路。
枚举每一条边,算出这样一个值,找到大于最短路的最小值即是次短路。
下面给出代码:
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cstdio>
#include <vector>
#define MAX 5005
#define INF 65535
using namespace std;
struct edge{ int to, cost;
edge( int t, int c ):to(t), cost(c){}
};
typedef pair<int,int> P;
int N, R;
int x, y, cost;
//int mat[MAX][MAX];
//int visit[MAX];
vector<edge> mat[MAX];
int lowCost0[MAX];// 1
int lowCostn[MAX];// n
struct cmp {
bool operator() ( const P& p1, const P& p2 ) {
return p1.first > p2.first;
}
};
void Dijkstra( int s, int* lowCost ) {
fill( lowCost+1, lowCost+N+1, INF );
lowCost[s] = 0;
priority_queue<P, vector<P>, cmp > que;
que.push( P(lowCost[s],s) );
while ( !que.empty() ) {
P p = que.top(); que.pop();
int k = p.second;
if ( p.first > lowCost[k] ) continue;
for ( int i = 0 ; i < mat[k].size() ; ++ i ) {
edge e = mat[k][i];
if ( lowCost[e.to] > lowCost[k]+e.cost ) {
lowCost[e.to] = lowCost[k]+e.cost;
que.push( P(lowCost[e.to], e.to) );
}
}
}
}
int main()
{
scanf( "%d%d", &N, &R );
for ( int i = 0 ; i < R ; ++ i ) {
scanf( "%d%d%d", &x, &y, &cost );
mat[x].push_back( edge(y,cost) );
mat[y].push_back( edge(x,cost) );
}
Dijkstra(1, lowCost0);// 第一个点到所有点的最短路的值
Dijkstra(N, lowCostn);// 第N个点到所有点的最短路的值
int shortCost = lowCost0[N];// 1->N的最短路的值
int secCost = INF;// 保存1->N的次短路的值
int tmp = 0;
for ( int i = 1 ; i <= N ; ++ i ) {
// 枚举每一条边
for ( int j = 0 ; j < mat[i].size() ; ++ j ) {
edge e = mat[i][j];
// 试试看不要这条边e(x,y)是不是次短路
// 这条路的值是 lowCost0[x] + e(x,y) + lowCostn[y]
// 即起点到x的最短路+终点到y的最短路+这条边的权值
tmp = lowCost0[i] + e.cost + lowCostn[e.to];
if ( tmp > shortCost && tmp < secCost ) {
secCost = tmp;
}
}
}
cout << secCost << endl;
return 0;
}
在《挑战程序设计竞赛》一书中有提到更优化的做法。
再使用Dijkstra的过程中,不仅保留最短路,同时保留次短路,这样一次Dijkstra就可以算出单源最短路和次短路。
struct edge { int to, cost; };
typedef pair<int, int> P; // first是最短距离 second是顶点编号
// 输入
int N, R;
vector<edge> G[MAX_N]; // 图的邻接表
int dist[MAXN_N]; // 最短距离
int dist2[MAX_N]; // 次短距离
void solve() {
// 通过指定greater<P> 参数, 堆按照first从小到大的顺序取出值
priority_queue<P, vector<P>, greater<P> > que;
fill(dist, dist+N, INF);
fill(dist2, dist2+N, INF);
dist[0] = 0;
que.push(P(0, 0));
while ( !que.empty() ) {
P p = que.top(); que.pop();
int v = p.second, d = p.first;
if ( dist2[v] < d ) continue;// 比当前次短路还长 就不需要考虑了
for ( int i = 0 ; i < G[v].size() ; ++ i ) {
edge &e = G[v][i];
int d2 = d + e.cost;
// 如果d2比当前最短路还短 那么就更新最短路 同时加入队列对其它数据进行更新
if ( dist[e.to] > d2 ) {
swap(dist[e.to], d2);
que.push(P(dist[e.to], e.to));
}
// 如果d2大于最短路但比次短路小 那就更新当前次短路 同时加入队列对其它数据进行更新
if ( dist2[e.to] > d2 && dist[e.to] < d2 ) {
dist2[e.to] = d2;
que.push(P(dist2[e.to], e.to));
}
}
}
printf( "%d\n", dist2[N-1] );
}