zoukankan      html  css  js  c++  java
  • 道路和航线

    https://loj.ac/problem/10081

    题目描述

      一张图,有由T个节点,R条双向边和P条单向边组成,每条边有边权且可能存在负权,求能否从S到达每个节点并且输出最小花费。

    思路

      首先第一想法,把这张图建出来,在跑一遍spfa即可,然而它显然被卡了,不过似乎可以用双端队列优化水过去。

      接下就是正解。我们考虑对于所有的道路,都是正权,我们可以用Dijkstra做,而对于航线,不存在环。因此,如果我们将道路相连的连通图缩点之后,得到必定是DAG,而DAG显然可以用拓扑排序更新最短路。

      所以我们只需要先把双向边的图建出来,跑一遍tarjan缩点,再进行toposort,每次更新时把连通块中所有节点都加入Dijkstra的优先队列中,对于每个点进行更新dis数组。在进行Dijkstra时把单向边也更新一遍即可。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=250020,M=1e5+10;
    const int INF=0x3f3f3f3f;
    
    int head[N],tot,to[M],nxt[M],w[M];
    void add_edge(int x,int y,int z)    //双向边建图 
    {
        nxt[++tot]=head[x];
        head[x]=tot;
        to[tot]=y;
        w[tot]=z;
    }
    int dfn[N],top,idx,st[N],low[N];
    int co[N],col;
    vector<int>g[N];
    bool vis[N];
    void tarjan(int u)        //tarjan缩点 
    {
        dfn[u]=low[u]=++idx;
        st[++top]=u;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u])
        {
            co[u]=++col;
            g[col].push_back(u);    //记录每个连通块的节点 
            while(st[top]!=u)
            {
                co[st[top]]=col;
                g[col].push_back(st[top]);
                top--;
            }
            top--;
        }
    }
    int dis[N];
    priority_queue<pair<int,int> >q;
    queue<int> que;
    int fir[N],cnt,nex[M],ed[M],c[M],in[N];
    void add(int x,int y,int z)        //单向边建图 
    {
        nex[++cnt]=fir[x];
        fir[x]=cnt;
        ed[cnt]=y;
        c[cnt]=z;
    }
    void Dijkstra()
    {
        while(!q.empty())
        {
            int u=q.top().second;q.pop();
            if(vis[u])continue ;
            vis[u]=1;
            for(int i=head[u];i;i=nxt[i])
            {
                int v=to[i];
                if(dis[u]<INF&&dis[v]>dis[u]+w[i])
                {
                    dis[v]=dis[u]+w[i];
                    q.push(make_pair(-dis[v],v));
                }
            }
            for(int i=fir[u];i;i=nex[i])    //更新单向边 
            {
                int v=ed[i];
                if(dis[u]<INF&&dis[v]>dis[u]+c[i])
                    dis[v]=dis[u]+c[i];            //不加入优先队列 
                in[co[v]]--;
                if(!in[co[v]])que.push(co[v]);
            }
        }
    }
    void toposort(int s)
    {
        memset(dis,0x3f,sizeof(dis));
        for(int i=1;i<=col;i++)
            if(!in[i])que.push(i);
        dis[s]=0;
        while(!que.empty())
        {
            int u=que.front();que.pop();
            for(int i=0;i<g[u].size();i++)        //更新连通块 
            {
                int v=g[u][i];
                q.push(make_pair(-dis[v],v));
            }
            Dijkstra();
        }
    }
    
    int read()
    {
        int res=0,w=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
        return res*w;
    }
    void write(int x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9)write(x/10);
        putchar(x%10^48);
    }
    void writeln(int x)
    {
        write(x);
        putchar('
    ');
    }
    
    int main()
    {
    //    freopen("a.txt","r",stdin);
    //    freopen("aa.txt","w",stdout);
        int t=read();
        int r=read(),p=read(),s=read();
        for(int i=1;i<=r;i++)
        {
            int u=read(),v=read(),w=read();
            add_edge(u,v,w);add_edge(v,u,w);
        }
        for(int i=1;i<=t;i++)
            if(!dfn[i])tarjan(i);
        for(int i=1;i<=p;i++)
        {
            int u=read(),v=read(),w=read();
            add(u,v,w);
            if(co[u]!=co[v])in[co[v]]++;
        }
        toposort(s);
        for(int i=1;i<=t;i++)
            if(dis[i]==INF)puts("NO PATH");
            else writeln(dis[i]);
    }
  • 相关阅读:
    暑期测试训练3
    对于在线段树上修改整段区间的理解
    UVA 11090 判负圈问题
    ZOJ 2588 求割边问题
    POJ 1523 网络连通
    hdu 1163
    hdu 1703
    hdu 2577 模拟
    hdu 3836 强连通+缩点:加边构强连通
    hdu 2571
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11687496.html
Copyright © 2011-2022 走看看