【BZOJ2200】道路和航线

这是一道非常不错的题目,融合了多种算法。

首先这是一道单源最短路问题,一个显然的做法就是直接用相关的算法解决,但是本题有负权边,所以我们不能用dijstra算法,只能用spfa,但是如果数据是精心设计,那么spfa算法很可能被卡死,所以简单的单源最短路算法无法直接通过本题。

仔细分析,这道题有一个特点:双向边都没有负权,负权只会出现在单向边当中,因此我们把这张图划分成若干个仅由双向边组成的联通块,显然他们都是强联通的,而单向边连接两个连通块构成了一个有向无环图,我们先在每一个连通块内做dijstra,此时边权无负数,所以可行,我们再用拓扑排序处理连通块之间的问题,问题就解决了。

具体地讲,我们读入双向边之后进行dfs,求出每一个点属于哪一个联通块,接着读入单向边并且统计每一个联通块的入度(把一个联通块看成一个点),方便拓扑排序。把源点所属的“块”和入度为0的“块”入队,进行拓扑排序,在处理每一个“块”时,进行dijstra,如果扩展的点和被扩展的点属于同一个联通块,那么接着进行dijstra,否则就按照拓扑排序的步骤,将其入度减一,判断是否为零入队……

最后,我们扫一遍每一个点,判断即可。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <queue>
 6 using namespace std;
 7 #define inf 0x3f3f3f3f
 8 inline int read() {
 9     int ret=0;
10     int op=1;
11     char c=getchar();
12     while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();}
13     while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
14     return ret*op;
15 }
16 struct node {
17     int next,to,dis;
18 }a[200010];
19 int num,head[200010];
20 int t,r,p,s;
21 inline void add(int from,int to,int dis) {
22     a[++num].next=head[from]; a[num].to=to; a[num].dis=dis; head[from]=num;
23 }
24 int be[25010],tot,deg[25010];
25 int vis[25010],dis[25010];
26 queue<int> q;
27 priority_queue<pair<int,int> >Q;
28 void dfs(int u) {
29     for(int i=head[u];i;i=a[i].next) {
30         int v=a[i].to;
31         if(!be[v]) {
32             be[v]=be[u];
33             dfs(v);
34         }
35     }
36 }
37 int main() {
38     t=read(); r=read(); p=read(); s=read();
39     for(int i=1;i<=r;i++) {
40         int x=read(),y=read(),z=read();
41         add(x,y,z);
42         add(y,x,z);
43     }
44     for(int i=1;i<=t;i++)
45         if(!be[i]) {
46             be[i]=++tot;
47             dfs(i);
48         }
49     for(int i=1;i<=p;i++) {
50         int x=read(),y=read(),z=read();
51         add(x,y,z);
52         deg[be[y]]++;
53     }
54     memset(vis,0,sizeof(vis));
55     memset(dis,0x7f,sizeof(dis));
56     dis[s]=0;
57     q.push(be[s]);
58     for(int i=1;i<=tot;i++)
59         if(!deg[i]) q.push(i);
60     while(!q.empty()) {
61         int now=q.front();
62         q.pop();
63         for(int i=1;i<=t;i++)
64             if(be[i]==now) Q.push(make_pair(-dis[i],i));
65         while(!Q.empty()) {
66             int u=Q.top().second;
67             Q.pop();
68             if(vis[u]) continue ;
69             vis[u]=1;
70             for(int j=head[u];j;j=a[j].next) {
71                 int k=a[j].to;
72                 if(dis[k]>dis[u]+a[j].dis) {
73                     dis[k]=dis[u]+a[j].dis;
74                     if(be[u]==be[k]) Q.push(make_pair(-dis[k],k));
75                 }
76                 if(be[u]!=be[k]&&--deg[be[k]]==0) q.push(be[k]);
77             }
78         }
79     }
80     for(int i=1;i<=t;i++)
81         if(dis[i]>inf) puts("NO PATH");
82         else printf("%d\n",dis[i]);
83     return 0;
84 }
AC Code

 

posted @ 2019-04-26 20:12  AD_shl  阅读(478)  评论(0编辑  收藏  举报