次短路 poj3255

       所谓两点之间的次短路:

    称path(u,v)为u,v之间的次短路,path(u,v)的权值大于(u,v)的最短路,且小于(u,v)的其余路径的权值(如果有的话)。

    如何找次短路?它要么是源点到某点u的最短路 + val(u,v),要么是源点到u的次短路 + val(u,v)!!!

    承认了这个结论后,可以如下设计算法:

用dijkstra搜路径,回顾一下dijkstra算法:

初始的时候有两个集合:S(已经求出最短路的点集),T(仍未求出最短路的点集),每次都从T中选出点u,点u是当前从源点到T中所有点距离最小的点,把u点加入S集合,然后用u点更新源点到T中点的距离,如此往复,直到T为空集

在用堆优化实现dijkstra的时候,会有这样一句if(vis[u])continue;以判断这个点是否已在S集合中,如果vis[u] = true则说明点u已在S中,更重要的是说明:这个点的已用他的最短路更新他所能到达的点的距离了,这时候可以直接跳过更新其他点的距离这一过程,因为这时候从堆中取出来的距离d<=u的最短路,松弛操作不会成功!!!而在求次短路的时候不能这样做,因为这个这时候从堆中取出来的距离d不能更新最短路但是他也许可以更新他所能到达的点次短路。

最后更新次短路的时候只要判断它比当前最短路大且小于当前次短路就可以用它来更新次短路,算法正确性,看代码就觉得很对了:

   

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define LL long long
const int maxn = 100055;
LL inf = 1e18;
struct Edge
{
    int v,val;
    int next;
}edge[maxn<<1];
int head[maxn>>1],cnt_e,vis[maxn];
struct P
{
    LL d;
    int u;
    P(LL d,int u):d(d),u(u){}
    bool operator < (const P &a)const {
        return d > a.d;
    }
};
LL dis1[maxn],dis2[maxn];
void add_edge(int u,int v,int val)
{
    edge[cnt_e].v = v;
    edge[cnt_e].val = val;
    edge[cnt_e].next = head[u];
    head[u] = cnt_e++;
}
void dijkstra(int n)
{
    memset(vis,0,sizeof(vis));
    priority_queue<P>pq;
    for(int i = 1;i<=n;i++){
        dis1[i] = dis2[i] = inf;
    }
    dis1[1] = 0;
    pq.push(P(dis1[1],1));
    while(!pq.empty()){
        P p = pq.top();
        pq.pop();
        LL d = p.d;
        int u = p.u;
        ///cout << d << " " << u<< endl;
        if(d>dis2[u])continue;
        for(int k = head[u];k!=-1;k = edge[k].next){
            int v = edge[k].v,val = edge[k].val;
            if(dis1[v] > d + val){
                dis1[v] = d + val;
                pq.push(P(dis1[v],v));
            }
            if(dis2[v] > d + val && dis1[v] < d + val){
                dis2[v] = d + val;
                pq.push(P(dis2[v],v));
            }
        }
    }
}
int main()
{
    int n,r,u,v,val;
    ////freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&r)!=EOF){
        cnt_e = 0;
        memset(head,-1,sizeof(head));
        for(int i = 0;i<r;i++){
            scanf("%d%d%d",&u,&v,&val);
            add_edge(u,v,val);
            add_edge(v,u,val);
        }
        dijkstra(n);
        printf("%lld\n",dis2[n]);
    }
    return 0;
}

 

posted on 2015-12-04 21:08  mathfinder  阅读(199)  评论(0编辑  收藏  举报

导航