「BZOJ2200」[Usaco2011 Jan] 道路和航线 - 最短路+拓扑排序

->点我进原题

[Usaco2011 Jan]道路和航线


Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 1116 Solved: 410


Description

Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查。他想把牛奶送到T个城镇 (\(1 <= T <= 25,000\)),编号为\(1\)~\(T\)。这些城镇之间通过\(R\)条道路 (\(1 <= R <= 50,000\),编号为\(1\)\(R\)) 和\(P\)条航线 (\(1 <= P <= 50,000\),编号为\(1\)\(P\)) 连接。每条道路i或者航线i连接城镇\(A_i\) (\(1 <= A_i <= T\))到\(B_i\) (\(1 <= B_i <= T\)),花费为\(C_i\)。对于道路,\(0 <= C_i <= 10,000\);然而航线的花费很神奇,花费\(C_i\)可能是负数(\(-10,000 <= C_i <= 10,000\))。道路是双向的,可以从\(A_i\)\(B_i\),也可以从\(B_i\)\(A_i\),花费都是\(C_i\)。然而航线与之不同,只可以从\(A_i\)\(B_i\)。事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台 了一些政策保证:如果有一条航线可以从\(A_i\)\(B_i\),那么保证不可能通过一些道路和航线从\(B_i\)回到\(A_i\)。由于FJ的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。他想找到从发送中心城镇\(S\)(\(1 <= S <= T\)) 把奶牛送到每个城镇的最便宜的方案,或者知道这是不可能的。

Input

\(1\)行:四个空格隔开的整数: \(T\), \(R\), \(P\), and \(S\)
\(2到R+1\)行:三个空格隔开的整数(表示一条道路):\(A_i\), \(B_i\)\(C_i\)
\(R+2到R+P+1\)行:三个空格隔开的整数(表示一条航线):\(A_i\), \(B_i\)\(C_i\)

Output

\(1到T\)行:从\(S\)到达城镇\(i\)的最小花费,如果不存在输出"NO PATH"。

Sample Input

6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10

样例输入解释:

一共六个城镇。在\(1-2,3-4,5-6\)之间有道路,花费分别是\(5,5,10\)。同时有三条航线:\(3\)->\(5\)
\(4\)->\(6\)\(1\)->\(3\),花费分别是-\(100\),-\(100\),-\(10\)\(FJ\)的中心城镇在城镇\(4\)

Sample Output

NO PATH
NO PATH
5
0
-95
-100

样例输出解释:

\(FJ\)的奶牛从\(4\)号城镇开始,可以通过道路到达\(3\)号城镇。然后他们会通过航线达到\(5\)\(6\)号城镇。
但是不可能到达\(1\)\(2\)号城镇。

分析

\(1\):Dijkstra+Topsort

本题是一道明显的单源最短路问题,但图中带有负权边,不能使用Dijkstra算法。若直接用SPFA算法求解,因为测试数据经过了特殊构造,所以程序无法在规定时限内输出答案。题目中有一个特殊条件——双向边都是非负的,只有单向边可能是负的,并且单向边不构成环。我们应该利用这个性质来解答本题。
如果只把双向边(道路)添加到图里,那么会形成若干个连通块。若把每个连通块整体看作一个“点”,再把单向边(航线)添加到图里,会得到一张有向无环图。在有向无环图中,无论边权正负,都可以按照拓扑排序进行扫描,在线性时间内求出单源最短路。这启发我们用拓扑排序的框架处理整个图,但在双向边构成的每个连通块内部使用堆优化的Dijkstra算法快速计算该块内的最短路信息
这样就可以在块内使用Dijkstra,块间利用拓扑排序更新答案。时间复杂度\(O(MlogN)\)

\(2\):SPFA+SLF

顺便加上了一些卡常的奇技淫巧,最慢的点为732ms,如果不了解SLF优化的可以看我的另外一篇博客->戳我

代码

\(1\)

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<queue>
#include<bits/stdc++.h>
#define rg register
using namespace std;
inline int read(){
    rg int f=0,x=0;
    rg char ch=getchar();
    while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    while(isdigit(ch))  x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

const int N =25010;
const int M =100010;
const int inf =0x7f7f7f7f;
#define ft first
#define sd second
int n,r,p,s,head[N],tot;
int cnt,belong[N];
int indeg[N],dis[N];
bool vis[N];
typedef pair<int ,int > pa;
vector <pa >road[N],plane[N];
vector <int >block[N];
inline void init(){
    for(rg int i=0;i<N;++i) dis[i]=inf;
    dis[s]=0;
}
inline void dfs(rg int u){
    belong[u]=cnt;
    block[cnt].push_back(u);
    for(int i=0;i<road[u].size();++i){
        int v=road[u][i].ft;
        if(!belong[v])  dfs(v);
    }   
}
inline void topsort_dij(){
    init();
    queue<int > q;
    for(rg int i=1;i<=cnt;++i)  if(!indeg[i])   q.push(i);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        priority_queue<pa,vector<pa>,greater<pa> > pq;
        for(int i=0;i<block[u].size();++i)
            if(dis[block[u][i]]!=inf)
                pq.push(make_pair(dis[block[u][i]],block[u][i]));
        while(!pq.empty()){
            int u=pq.top().sd;
            pq.pop();
            if(vis[u]) continue;
            vis[u]=true;
            for(int i=0;i<road[u].size();++i)
                if(dis[u]+road[u][i].sd<dis[road[u][i].ft])
                    pq.push(make_pair(dis[road[u][i].ft]=dis[u]+road[u][i].sd,road[u][i].ft));
            for(int i=0;i<plane[u].size();++i)
                dis[plane[u][i].ft]=min(dis[plane[u][i].ft],dis[u]+plane[u][i].sd);
        }
        for(int i=0;i<block[u].size();++i)
            for(int j=0;j<plane[block[u][i]].size();++j)
                if(--indeg[belong[plane[block[u][i]][j].ft]]==0)
                    q.push(belong[plane[block[u][i]][j].ft]);
    }
}
signed main(){
    n=read(),r=read(),p=read(),s=read();
    for(rg int i=1,u,v,w;i<=r;++i){
        u=read(),v=read(),w=read();
        road[u].push_back(make_pair(v,w));
        road[v].push_back(make_pair(u,w));
    }
    for(rg int i=1;i<=n;++i)
        if(!belong[i]){
            ++cnt;
            dfs(i);
        }
    for(rg int i=1,u,v,w;i<=p;++i){
        u=read(),v=read(),w=read();
        plane[u].push_back(make_pair(v,w));
        ++indeg[belong[v]];
    }
    topsort_dij(); 
    for(rg int i=1;i<=n;++i)
        if(dis[i]==inf) printf("NO PATH\n");
        else    printf("%d\n",dis[i]);
    return 0;
}

\(2\)

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<queue>
#define rg register
using namespace std;
inline int read(){
    rg int f=0,x=0;
    rg char ch=getchar();
    while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    while(isdigit(ch))  x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

const int N =25010;
const int M =150010;
const int inf =0x7f7f7f7f;
int n,r,p,s,head[N],tot,dis[N];
bool vis[N];
struct edge{
    int to,nxt,w;
}e[M];
inline void add(rg int u,rg int v,rg int w){
    e[++tot].to=v;
    e[tot].w=w;
    e[tot].nxt=head[u];
    head[u]=tot;
}
inline void spfa(rg int s){
    for(rg int i=1;i<=n;++i)    dis[i]=inf;
    dis[s]=0;
    deque<int > q;
    q.push_back(s);
    while(!q.empty()){
        int u=q.front();
        q.pop_front();
        vis[u]=false;
        for(rg int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(dis[v]>dis[u]+e[i].w){
                dis[v]=dis[u]+e[i].w;
                if(!vis[v]){
                    vis[v]=true;
                    if(q.empty()||dis[v]>dis[q.front()])    q.push_back(v);
                    else    q.push_front(v);
                }
            }
        }
    }
    
}
signed main(){
    n=read(),r=read(),p=read(),s=read();
    for(rg int i=1;i<=r;++i){
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    for(rg int i=1;i<=p;++i){
        int u=read(),v=read(),w=read();
        add(u,v,w);
    }
    spfa(s);
    for(rg int i=1;i<=n;++i)
        if(dis[i]==inf) printf("NO PATH\n");
        else    printf("%d\n",dis[i]);
    return 0;
}

转载于:https://www.cnblogs.com/horrigue/p/9641636.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值