次短路(poj 3255 Roadblocks)

题目链接: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

Line 1: Two space-separated integers:  N and  R 
Lines 2.. R+1: Each line contains three space-separated integers:  AB, and  D that describe a road that connects intersections  A and  B and has length  D (1 ≤  D ≤ 5000)

Output

Line 1: The length of the second shortest path between node 1 and node  N

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] );
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值