zoukankan      html  css  js  c++  java
  • bzoj1726: [Usaco2006 Nov]Roadblocks第二短路

    【题目大意】

    求无向图点1到n的次短路。

    【思路】

    堆优化Dijkstra,方法就是一边跑Dijsktra一边就把次短路径保存下来。和一般Dijkstra不同的是把vis数组去掉了,因为还要生成次短路径。

    先来谈谈Dijkstra的优化。对于每次寻找到当前为访问过的点中距离最短的那一个,运用优先队列进行优化,避免全部扫描,每更新一个点的最短距离就加入优先队列。有人会问,一个点如果已经处理完成了,那它还留在队列中怎么办?我们放入队列时将一个点那时的顶点编号和最短距离进行打包,如果取出该点时,它当前的最短距离小于该点标记的最短距离,说明该点已经取到最短距离,不进行操作。或者直接用一个vis数组来记录某一个点是否已经取到最短距离;其次的优化是用邻接表存储与每一个点相连的所有边,方便处理。

    这道题的做法和最短路径基本一致,唯一的不同点在于,在求出最短路径的情况下必须要保留下次短路径。对于Dijkstra判断中取出的每一个点,如果到它的最短距离大于当前该点的次短距离,则当前该点已经取到最短距离和次短距离,不进行操作,否则进行两次判断:如果小于最短边,则赋给最短变,并将最短边赋给次短边;或者如果大于最短变且小于次短边,则赋给次短边。两次完成之后均要加入队列。要注意几点:

    (1)由于是一张无向图,读入的时候必须正向、逆向分为两条边存储,所以实际有向边的数量为r的两倍,数组绝对不能开小!我就因为这个错拿了90分,还检查了三个小时后找到测试数据才意识到的...

    (2)初始化时,源点的短边初始化为0,源点的次短边必须初始化为INF,而不是0。比如下面这组数据:

    4 2
    1 2 100
    2 4 200
    答案应该是500,然而如果初始化为0则答案会输出700。因为500的结果是又1到2,在从2返回1,再到2,再到4,100+100+100+200=500得到的;如果次短边初始化为0,则次短路径不再返回源点,而是在2与4之间折返,会偏大。
    (3)53行绝对不能直接赋值,而是要swap!因为最短边被修改后,它的值是要留给次短边的。
    补充
    (1)要注意if (head.len>secondis[head.num]) continue;的位置!我两次写的时候都把它放在了while(k!=-1)里面然后判断(d>secondis[v[k]])这是不对的!因为如果这个时候continue,k的值就无法改变,会导致死循环。
    (2)要注意优先队列默认是大顶堆,struct里面设置的时候前后大于小于号必须要相反才能设置成小顶堆!
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cstdlib>
    using namespace std;
    const int INF=0x7fffffff;
    const int MAXN=100000+10;
    struct Rec
    {
        int num,len;
        bool operator < (const Rec &a) const
        {
            return len>a.len;
            /*优先队列强制设为以len为关键词的小顶堆*/
        }
    }; 
    int u[MAXN*2],v[MAXN*2],w[MAXN*2];
    /*依次表示每条道路的起点、终点和长度*/
    int dis[MAXN/2],secondis[MAXN/2];
    /*记录通往每一个路口的最短和次短距离*/
    int first[MAXN/2],next[MAXN*2];
    int n,r;
    
    void dijkstra()
    {
        priority_queue<Rec> que;
        
        for (int i=1;i<n;i++)
        {
             dis[i]=INF;
             secondis[i]=INF;
        }
        dis[0]=0;
        secondis[0]=INF;
        
        Rec temp;
        temp.len=0;temp.num=0;
        que.push(temp);
        
        while (!que.empty())
        {
            Rec head=que.top();
            que.pop();
            if (head.len>secondis[head.num]) continue;
            
            int k=first[head.num];
            while (k!=-1)
            {
                int d=head.len+w[k];
                if (dis[v[k]]>d)
                {
                    swap(dis[v[k]],d);
                    temp.len=dis[v[k]];temp.num=v[k];
                    que.push(temp);
                }
                if (dis[v[k]]<d && secondis[v[k]]>d)
                {
                    secondis[v[k]]=d;
                    temp.len=secondis[v[k]];temp.num=v[k];
                    que.push(temp);
                }
                k=next[k];
            }
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&r);
        memset(first,-1,sizeof(first));
        for (int i=0;i<r;i++)
        {
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
            u[i]--;
            v[i]--;
            next[i]=first[u[i]];
            first[u[i]]=i;
            
            v[i+r]=u[i];
            u[i+r]=v[i];
            w[i+r]=w[i];
            next[i+r]=first[u[i+r]];
            first[u[i+r]]=i+r;
        }
        dijkstra();
        cout<<secondis[n-1]<<endl;
        return 0;
    }

    重新写用的是SPFA。正反跑两次SPFA,然后枚举每一条边,如果起点到一个端点的最短路+另一个端点到终点的最短路+长度 ≠ 最短路,则和答案比较,保存最小值。还是很好理解的

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXR=1e5+5;
    const int MAXN=5e3+5;
    const int INF=21e8;
    struct edge
    {
        int to,len;
    };
    vector<edge> E[MAXN];
    int n,r,dis[MAXN],inque[MAXN],dis1[MAXN],dis2[MAXN];
    int u[MAXR],v[MAXR],w[MAXR];
    void addedge(int u,int v,int w)
    {
        E[u].push_back((edge){v,w});
        E[v].push_back((edge){u,w});
    }
    void spfa(int S,int T)
    {
        for (int i=1;i<=n;i++) inque[i]=0,dis[i]=INF;
        queue<int> que;
        que.push(S);
        inque[S]=1;dis[S]=0;
        while (!que.empty())
        {
            int head=que.front();que.pop();
            inque[head]=0;
            for (int i=0;i<E[head].size();i++)
            {
                int nowdis=dis[head]+E[head][i].len,to=E[head][i].to;
                if (nowdis<dis[to])
                {
                    dis[to]=nowdis;
                    if (!inque[to])
                    {
                        que.push(to);
                        inque[to]=1;
                    }
                }
            }
        }
    }
    int main()
    {
        scanf("%d%d",&n,&r);
        for (int i=1;i<=r;i++)
        {
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
            addedge(u[i],v[i],w[i]);
        }
        spfa(1,n);
        for (int i=1;i<=n;i++) dis1[i]=dis[i];
        spfa(n,1);
        for (int i=1;i<=n;i++) dis2 [i]=dis[i];
        int mx=dis1[n],ans=INF;
        for (int i=1;i<=r;i++)
        {
            int now=dis1[u[i]]+dis2[v[i]]+w[i];
            if (now!=mx) ans=min(ans,now);
            now=dis1[v[i]]+dis2[u[i]]+w[i];
            if (now!=mx) ans=min(ans,now);
        }
        printf("%d",ans);
        return 0;    
    }
  • 相关阅读:
    emacs 探索之六:latex中文支持
    One网络模拟器探索之六:Report类的扩展
    emacs 探索之五:latex配置
    emacs 探索之三:基本操作
    DataSet数据传送性能比较
    SQL 2008 附加数据库报5120的错误的解决办法
    软件工程师不可不知的10个概念
    在日期上加上相应天数,并在GridView上显示
    SQL 跨表更新
    SQLSERVER 处理两个日期相减
  • 原文地址:https://www.cnblogs.com/zzrblogs/p/10458390.html
Copyright © 2011-2022 走看看