zoukankan      html  css  js  c++  java
  • [LOJ3255][JOI 2020 Final]奥运公交(最短路)

    [LOJ3255][JOI 2020 Final]奥运公交(最短路)

    题面

    给出一个(n)个点(m)条边的有向图,经过每条边需要费用(c_i).选择一条边并将其反向需要费用(d_i)(反向后经过的费用不变).问至多反向一条边,从1到n再回到1的最小花费

    (n leq 200,m leq 50000)

    分析

    考虑枚举反向的边(i=(u,v)),经过的费用为(w),反转的费用为(c).记(d(u,v))表示(u)(v)的最少费用,(f(i,u,v))表示不经过边(i)(u)(v)的最短路
    那么

    [d(1,n)=min(f(i,1,n),f(i,1,v)+w+f(i,u,n)) ]

    [d(n,1)=min(f(i,n,1)),f(i,n,v)+w+f(i,u,1)) ]

    这是因为从(1)(n)有2种选择:不经过((u,v))直接走到(n), 或者先到(v),经过((v,u))再到(n). 从(n)(1)的情况同理。总费用为(d(1,n)+d(n,1)+c)

    那么我们考虑如何求出(f).我们发现起点和终点只会是(1)(n).不妨考虑起点为1的情况。求出(operatorname{dist}(i))表示原图起点到(i)的费用,并求出一棵最短路树(T)

    [f(i,1,v)=egin{cases}operatorname{dist}(v),(u,v) otin T \ ext{原图上不经过}i ext{从原点到}v ext{的最小费用},(u,v)in Tend{cases} ]

    对于第一种情况直接开始时预处理即可。第二种情况需要重新跑一次最短路。注意到最短路树上只有(n-1)条边,第二种情况会出现(O(n))次。如果用堆优化的Dijkstra,单次复杂度为(O((n+m)log n)),因为(m)达到了(n^2)级别,总复杂度(O((n^2+nm)log n)=O(n^3log n)),,需要较强的常数优化才能通过。但是无堆优化的Dijkstra复杂度是(O(n^2+m))且常数很小,总复杂度(O(n^3))可以通过本题。

    其他的(f)同理可求:(f(i,u,n))(n)出发在原图的反图上跑最短路,(f(i,n,u))(n)出发在原图上跑最短路,(f(i,u,1))(1)出发在原图的反图上跑最短路. 代码可复用的部分较多,注意封装。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define maxm 50000
    #define maxn 200
    #define INF 0x3f3f3f3f3f3f3f3f 
    using namespace std;
    typedef long long ll;
    int n,m;
    struct Graph{
        int S;
        struct edge{
            int from;
            int to;
            int len;
            int cost;
            int next;
        }E[maxm+5]; 
        int head[maxn+5];
        int esz;
        void add_edge(int u,int v,int w,int c){
            esz++;
            E[esz].from=u;
            E[esz].to=v;
            E[esz].len=w;
            E[esz].cost=c;
            E[esz].next=head[u];
            head[u]=esz;
        }
    
        ll dist1[maxn+5];
        int last[maxn+5];
        bool on_tree[maxm+5]; 
        void dijkstra(){//O(n^2)的Dijkstra,排除m的影响 
            static bool vis[maxn+5];
            memset(dist1,0x3f,sizeof(dist1));
            memset(vis,0,sizeof(vis));
            dist1[S]=0; 
            for(int p=1;p<=n;p++){
                int x=-1;
                for(int i=1;i<=n;i++) if(!vis[i]&&(x==-1||dist1[i]<dist1[x])) x=i;
                if(x==-1) break;
                vis[x]=1;
                for(int i=head[x];i;i=E[i].next){
                    int y=E[i].to;
                    if(dist1[y]>dist1[x]+E[i].len){
                        dist1[y]=dist1[x]+E[i].len;
                        last[y]=i;
                    }
                }
            }
            for(int i=1;i<=n;i++) if(i!=S) on_tree[last[i]]=1;
        }
    
        ll dist2[maxn+5];
        void dijkstra2(int bane){
            static bool vis[maxn+5];
            memset(dist2,0x3f,sizeof(dist2));
            memset(vis,0,sizeof(vis));
            dist2[S]=0;
            for(int p=1;p<=n;p++){
                int x=-1;
                for(int i=1;i<=n;i++) if(!vis[i]&&(x==-1||dist2[i]<dist2[x])) x=i;
                if(x==-1) break;
                vis[x]=1;
                for(int i=head[x];i;i=E[i].next){
                    int y=E[i].to;
                    if(i==bane) continue;//由于有重边,不能用端点来判断 
                    if(dist2[y]>dist2[x]+E[i].len) dist2[y]=dist2[x]+E[i].len;
                } 
            }
        }
        ll calc(int eid,int x){//不经过边i的1到x最短路 
            if(!on_tree[eid]) return dist1[x];
            else{//重新跑一遍最短路 
                dijkstra2(eid);
                return dist2[x];
            }
        }
    }G[4];
    
    
    int main(){
        static int u[maxm+5],v[maxm+5],w[maxm+5],c[maxm+5];
        scanf("%d %d",&n,&m);
        G[0].S=1;G[1].S=n;G[2].S=1;G[3].S=n;
        for(int i=1;i<=m;i++){
            scanf("%d %d %d %d",&u[i],&v[i],&w[i],&c[i]);
            G[0].add_edge(u[i],v[i],w[i],c[i]); 
            G[1].add_edge(v[i],u[i],w[i],c[i]);//反图上n的最短路树,对应正图上i到n的路径 
            G[2].add_edge(v[i],u[i],w[i],c[i]);
            G[3].add_edge(u[i],v[i],w[i],c[i]);
        }
        for(int i=0;i<4;i++) G[i].dijkstra();
        ll ans=G[0].dist1[n]+G[3].dist1[1];
        for(int i=1;i<=m;i++){
            ll d1=min(G[0].calc(i,n)/*1直接绕过i到n*/,G[0].calc(i,v[i])/*1->v*/+w[i]+G[1].calc(i,u[i])/*u->n*/);
            ll d2=min(G[3].calc(i,1)/*n直接绕过i到1*/,G[3].calc(i,v[i])/*n->v*/+w[i]+G[2].calc(i,u[i])/*u->1*/);
            if(d1>=INF||d2>=INF) continue;
            ans=min(ans,d1+d2+c[i]);
        } 
        if(ans>=INF) ans=-1;
        printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    lightoj 1341 Aladdin and the Flying Carpet(算术基本定理)题解
    Bi-shoe and Phi-shoe(欧拉函数/素筛)题解
    HDU 2157(矩阵快速幂)题解
    SPOJ LAS(BFS)题解
    codevs 1106 篝火晚会
    codevs 1137 计算系数
    codevs 1171 潜伏者
    codevs 3732 解方程
    codevs 3290 华容道
    codevs 3289 花匠
  • 原文地址:https://www.cnblogs.com/birchtree/p/13412281.html
Copyright © 2011-2022 走看看