zoukankan      html  css  js  c++  java
  • 【BZOJ】2200: [Usaco2011 Jan]道路和航线

    【题意】给定n个点的图,正权无向边,正负权有向边,保证对有向边(u,v),v无法到达u,求起点出发到达所有点的最短距离。

    【算法】拓扑排序+dijkstra

    【题解】因为有负权边,直接对原图进行spfa,加slf优化后可过,但是这道题就没意思了。

    理论上,最短路问题用spfa是不能保证复杂度的,但dijkstra的问题是不能处理负权边。

    因为题目保证不能返回,实际上有向边将全图分成了几个部分。如果把仅由无向边连接的连通块看成点,则原图变成DAG。

    对连通块内部进行dijkstra,在DAG上用拓扑序递推计算就可以保证O(n log n)出解。

    具体实现:全图共用最短距离数组d[]。

    对跨越连通块的有向边建新图,先用部分拓扑序删掉不从s出发的点。

    开连通块个数的堆。

    然后拓扑排序的过程中将到达别的连通块的点加入对应的堆,dijkstra时直接开始不用设置初始状态。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    #include<queue>
    using namespace std;
    const int maxn=400010,inf=0x3f3f3f3f;
    int first[maxn],FIRST[maxn],d[maxn],col[maxn],in[maxn],A[maxn],B[maxn];
    int tot,cnt,n,m,N,M,s,color;
    struct edge{int v,w,from;}e[maxn*2],E[maxn*2];
    struct cyc{
        int x,d;
        bool operator < (const cyc &a)const{
            return d>a.d;//
        }
    };
    priority_queue<cyc>q[maxn];
    int read(){
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    void insert(int u,int v,int w){tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    void INSERT(int u,int v,int w){cnt++;E[cnt].v=v;E[cnt].w=w;E[cnt].from=FIRST[u];FIRST[u]=cnt;in[v]++;}
    void dfs(int x,int color){
        col[x]=color;
        for(int i=first[x];i;i=e[i].from)if(!col[e[i].v]){
            dfs(e[i].v,color);
        }
    }
    queue<int>Q;
    void dijkstra(int k){
        while(!q[k].empty()){
            cyc y=q[k].top();q[k].pop();
            if(y.d!=d[y.x])continue;
            int x=y.x;
            for(int i=first[x];i;i=e[i].from)if(d[e[i].v]>d[x]+e[i].w){
                d[e[i].v]=d[x]+e[i].w;
                q[k].push((cyc){e[i].v,d[e[i].v]});
            }
        }
    }    
    int main(){
        scanf("%d%d%d%d",&n,&m,&M,&s);
        for(int i=1;i<=m;i++){
             int u=read(),v=read(),w=read();
             insert(u,v,w);insert(v,u,w);
        }
        for(int i=1;i<=n;i++)if(!col[i])dfs(i,++N);
        for(int i=1;i<=M;i++){
            int u=read(),v=read(),w=read();
            INSERT(col[u],col[v],w);
            A[i]=u;B[i]=v;
        }
        for(int i=1;i<=N;i++)if(!in[i]&&col[s]!=i)Q.push(i);
        while(!Q.empty()){
            int x=Q.front();Q.pop();
            for(int i=FIRST[x];i;i=E[i].from){
                in[E[i].v]--;
                if(!in[E[i].v]&&col[s]!=E[i].v)Q.push(E[i].v);
            }
        }
        Q.push(col[s]);
        memset(d,0x3f,sizeof(d));d[s]=0;q[col[s]].push((cyc){s,0});
        while(!Q.empty()){
            int x=Q.front();Q.pop();
            dijkstra(x);
            for(int i=FIRST[x];i;i=E[i].from){
                if(d[B[i]]>d[A[i]]+E[i].w){
                    d[B[i]]=d[A[i]]+E[i].w;
                    q[E[i].v].push((cyc){B[i],d[B[i]]});
                }
                in[E[i].v]--;
                if(!in[E[i].v])Q.push(E[i].v);
            }
        }
        for(int i=1;i<=n;i++)if(d[i]<inf)printf("%d
    ",d[i]);else printf("NO PATH
    ");
        return 0;
    }            
    View Code

    dijkstra使用小根堆!每次加入距离最小的点。最致命的是写成大根堆也可以跑出答案,但是大数据就会很慢。

    dijkstra使用小根堆!

    dijkstra使用小根堆!

    dijkstra使用小根堆!

    dijkstra使用小根堆!

    dijkstra使用小根堆!

    dijkstra使用小根堆!

    dijkstra使用小根堆!

    dijkstra使用小根堆!

    dijkstra使用小根堆!

  • 相关阅读:
    DHCP和NAT
    Mongos WoW
    是否能在构造函数,析构函数中抛出异常?
    Lua Getter/Setter
    xLua
    基于模板特化的Lua自动绑定系统
    为什么有人说富坚义博有着殿堂级的画功?他的画功体现在哪儿呢? 关注者 10205 被浏览 2701670
    在Windows和MacOS下编译Lua
    解决warning MSB8012:问题
    setjmp和longjmp
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7695127.html
Copyright © 2011-2022 走看看