【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 }