挑战程序设计竞赛2.5例题:Roadblocks POJ - 3255 次短路问题

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)
本题是一个典型的次短路问题,用dijikstra求解最短路径大家都知道,但是次短路怎么算,实际上我们可以采用动态规划的想法,到达终点v的最短路要么是到达和v联通的u点的最短路加上u->v,要么就是到达u的次短路加上u->v,原因很简单,因为想要到达v,必定要先到达u(u指的是所有和v联通的某一个点),那么之前到达u的最短路加上u->v可能不是到达v的最小的了,所以找到次短路一定是在这些u点中的次短路、最短路加上u->v得到次短路,因为如果到达u还不是次短路,那么肯定比到达u的次短路要长,两者加上u->v这段相同的距离后,还是次短路短,所以我们只用考虑到次短路,那为什么不只考虑最短路呢?因为如果所有到u的最短路加上u->v之后值都相同,那么就不行了,因为题目要求次短路一定要比最短路要长。
至于次短路,我们可以逐一递推,直到第一个点,这样是不是很像dijikstra的方法,这个方法就是从第一个点推到至最后的。但是还是有点不同的,因为普通的操作只需要找到最小值即可不用再次查询已经拓展的点,但是次短路不同,最开始只能找到最短路,并不能保证第二小的路径是次短路,所以所有的可能都要进行考虑,也就是说,所有连通性都要考虑,但是我们还是可以剪枝的——如果从起始点到达i点的某条路径d[i]大于当前的次短路的话,那么d[i]一定不会影响到最终结果,所以我们可以直接继续遍历拓展下一个可能性。为什么等于次短路也可以继续拓展呢?因为当一个值不管是最短路还是次短路时,我们都要去拓展这个可能性,由于优先队列中存在某点大于等于最短路的值(因为如果小于最短路也存在的话,最短路就会被更新,所以不存在小于最短路的值),不管怎么样只要是小于等于次短路时,就是要去拓展为该值,当优先队列取出的值等于次短路时,那么就是之前更新该点的次短路的情况,我们当然要拓展这种情况,如果小于次短路,可能是之前被更新掉的最短路,由于其有可能是和其联通的结点变成新的次短路,所以我们也要更新,但如果大于的话,无论如何都不可能会被更新(因为u->v值一定,从出发点到达u的值大于次短路,两者加上相同值后还是原来次短路短,所以次短路也不会更新,所以不要)。
注意:当更新最短路时,其肯定不是次短路,但是要将原来的最短路与其进行交换使得判断次短路时采用原最短路值,这是因为当我们把一个值赋给最短路时,它肯定不能赋值给次短路,但是之后该值不一定再会出现(因为两点之间可能存在多条路径本次可能i更新多次,但是如果大的在前,小的在后,则无法将次小的赋值给次短路数组,导致本次i节点更新多次,虽然最短路答案没有问题,但是次短路会出错,但是交换可以使得本次i不管更新几次,都能够正确找到次短路,因为所有被交换的值(也就是在一次更新中,对于i节点处了最短路之外的所有值都能与次短路比较,这样可以次短路就不会错误了)
AC代码:
#include <stdio.h>
#include <vector>
#include <queue>
#include <algorithm> 
using namespace std;
const int INF = 0x3fffffff;
struct Node{
    int to;
    int value;
    Node(int x, int y){
        to = x;
        value = y;
    }
    friend bool operator <(Node x, Node y){
        return x.value > y.value;
    }
};
priority_queue<Node> pq;
int min_dis[5005], sec_dis[5005];//最短路,次短路
vector<Node> v[5005];//邻接表
int main(void)
{
    int n, r;
    int from, to, cost;
    scanf("%d %d", &n, &r);
    for(int i = 0; i < r; i++)
    {
        scanf("%d %d %d", &from, &to, &cost);
        v[from].push_back(Node(to, cost));
        v[to].push_back(Node(from, cost));
    }
    fill(min_dis, min_dis + n + 1, INF);
    fill(sec_dis, sec_dis + n + 1, INF);
    min_dis[1] = 0; //第一个点到自己为0
    pq.push(Node(1, 0));
    while(!pq.empty())
    {
        Node temp = pq.top();
        pq.pop();
        if(temp.value > sec_dis[temp.to])//如果大于次短路,那么没有必要更新
            continue;
        for(int i = 0; i < v[temp.to].size(); i++)
        {
            int d = v[temp.to][i].value + temp.value;
            if(d < min_dis[v[temp.to][i].to])
            {
                swap(min_dis[v[temp.to][i].to], d);
                pq.push(Node(v[temp.to][i].to, min_dis[v[temp.to][i].to]));
            }
            if(d > min_dis[v[temp.to][i].to] && d < sec_dis[v[temp.to][i].to])
            {
                sec_dis[v[temp.to][i].to] = d;
                pq.push(Node(v[temp.to][i].to, sec_dis[v[temp.to][i].to]));
            }
        }
    }
    printf("%d\n", sec_dis[n]);
    return 0;
}

 

posted @ 2020-02-02 21:19  funforever  阅读(200)  评论(0编辑  收藏  举报