码迷,mamicode.com
首页 > 其他好文 > 详细

道路和航线

时间:2019-10-16 19:59:12      阅读:102      评论:0      收藏:0      [点我收藏+]

标签:ret   char   因此   bool   push   name   putc   getc   ref   

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(\n);
}

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]);
}

 

道路和航线

标签:ret   char   因此   bool   push   name   putc   getc   ref   

原文地址:https://www.cnblogs.com/fangbozhen/p/11687496.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!