次短路

来源:互联网 发布:电视直播软件tv版 编辑:程序博客网 时间:2024/06/10 03:58

顾名思义: 求出这幅图中某点到某点的次短路. 怎么求了?
利用Dijkstra算法求解次短路

我们曾经学过利用Dijkstra算法求解最短路, 但是如果要求解某一个结点的次短路该怎么做呢?实际上,我们仍然可以用Dijkstra算法来求解它.

首先来回顾一下Dijkstra算法的原理: 首先把所有结点的最短距离设置为无穷大, 然后令d[1] = 0. (到其本身) 接下来,每次都找到最短路已经确定的经典, 更新从它出发的相邻结点的最短距离. 以后我们不再考虑最短距离已经确定了的结点.

以上就是Dijkstra算法的主要过程, 需要注意的一点是我们不再考虑的是 “最短距离已经确定了的结点”, 不要错误地理解为是更新过最短距离值的结点. 因为有些结点的最短路可能需要多次更新才能最终确定. 那么问题来了, 如何知道哪些结点的最短距离是确定的呢? 这里我们利用了一点贪心的思想, 每次都取出当前距离最短的那个结点, 认为它的最短路就是已经确定好的. 可以证明这样的做法是正确的. 这也算为什么优化版本的Dijkstra算法用到了priority_queue的原因.

所以次短路的做法就是:
其实总的做法是一样的, 唯一的不同点在于, 在求出最短路径的情况下必须要保留下次短路径。对于Dijkstra判断中取出的每一个点, 如果到它的距离大于当前该点的次短距离, 则当前该点已经取到最短距离和次短距离,不进行操作, 否则进行两次判断: 如果小于最短边, 则赋给最短边, 并将最短边赋给次短边;或者如果大于最短边且小于次短边, 则赋给次短边. 两次完成之后均要加入队列. 要注意几点:
1 : 储存的是双向边, 那么结构体应该开大2倍.
2 : 进入第一个if语句时, 不应该直接赋值而是要swap !!! 因为最短边被修改后,它的值是要留给次短边的.

板子: (边权大一点的也适用) 其实也是可以求出最短路, 相当于堆优化了的Dij.
//这个板子只适用于求单源次短路, 最短路.

/** @Cain*/#define ll long long inttypedef pair<ll,int>P;const ll INF = 1e18;const int maxn = 1e5 + 5;int cas = 1;int head[maxn];ll dis1[maxn],dis2[maxn];int n,m,cnt;struct node{    int to,next;    ll w;}s[maxn*2]; //两倍.void add(int u,int v,ll w){    s[cnt].to = v;    s[cnt].w = w;    s[cnt].next = head[u];    head[u] = cnt++;}void solve(){    while(~scanf("%d%d",&n,&m)){        cnt = 0; Fill(head,-1);        Fill(s,0);        for(int i=1;i<=m;i++){            int u,v;            ll w;            scanf("%d%d%lld",&u,&v,&w);            add(u,v,w); add(v,u,w);        }        priority_queue<P, vector<P>, greater<P> >q;        for(int i=1;i<=n;i++) dis1[i] = dis2[i] = INF;        dis1[1] = 0;  //次短路的初值就保持为无穷大.        q.push(P(0,1));        while(!q.empty()){            P tmp = q.top();            q.pop();            ll d = tmp.first; int u = tmp.second;            if(d > dis2[u] ) continue;               //说明u点的最短路径和次短路径已经被更新过了. 不用重复操作.            for(int i=head[u]; ~i ; i = s[i].next){                int to = s[i].to; ll d2 = d + s[i].w;                if(dis1[to] > d2){                    swap(dis1[to],d2);                         //不能直接赋值,而是要交换,因为原先的最短路值是要留着来更新次短路值的.                    //随便举一举例子就知道了.当然在这里判一下也是可以的,就可以直接赋值了.                    q.push(P(dis1[to],to));                }                if(dis2[to] > d2 && dis1[to] < d2){                    dis2[to] = d2;                    q.push(P(dis2[to],to));                }            }        }        printf("%lld\n",dis2[n]);    }}

POJ – 3255 模板题
AC Code

/** @Cain*/#define ll long long inttypedef pair<ll,int>P;const ll INF = 1e18;const int maxn = 1e5 + 5;int cas = 1;int head[maxn];ll dis1[maxn],dis2[maxn];int n,m,cnt;struct node{    int to,next;    ll w;}s[maxn*2];void add(int u,int v,ll w){    s[cnt].to = v;    s[cnt].w = w;    s[cnt].next = head[u];    head[u] = cnt++;}void solve(){    while(~scanf("%d%d",&n,&m)){        if(n +m == 0) break;        cnt = 0; Fill(head,-1);        Fill(s,0);        for(int i=1;i<=m;i++){            int u,v;            ll w;            scanf("%d%d%lld",&u,&v,&w);            add(u,v,w); add(v,u,w);        }        priority_queue<P, vector<P>, greater<P> >q;        for(int i=1;i<=n;i++) dis1[i] = dis2[i] = INF;        dis1[1] = 0;        q.push(P(0,1));        while(!q.empty()){            P tmp = q.top();            q.pop();            ll d = tmp.first; int u = tmp.second;            if(d > dis2[u] ) continue;            for(int i=head[u]; ~i ; i = s[i].next){                int to = s[i].to; ll d2 = d + s[i].w;                if(dis1[to] > d2){                    swap(dis1[to],d2);                    q.push(P(dis1[to],to));                }                if(dis2[to] > d2 && dis1[to] < d2){                    dis2[to] = d2;                    q.push(P(dis2[to],to));                }            }        }        printf("%lld\n",dis2[n]);    }}
原创粉丝点击