poj 2186(强连通分量入门题)
Posted violet-acmer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 2186(强连通分量入门题)相关的知识,希望对你有一定的参考价值。
https://www.cnblogs.com/violet-acmer/p/9739990.html
题意:
每头牛都想成为牛群中的红人。
给定N头牛的牛群和M个有序对(A, B),(A, B)表示牛A认为牛B是红人,该关系具有传递性,所以如果牛A认为牛B是红人,牛B认为牛C是红人,那么牛A也认为牛C是红人。
不过,给定的有序对中可能包含(A, B)和(B, C),但不包含(A, C)。
求被其他所有牛认为是红人的牛的总数。
分析:
考虑以牛为顶点的有向图,对每个有序对(A, B)连一条从 A到B的有向边,那么,被其他所有牛认为是红人的牛对应的顶点,也就是从其他所有顶点都可达的顶点。
虽然这可以通过从每个顶点出发搜索求得,但总的复杂度却是O(NM),是不可行的,必须要考虑更为高效的算法。
假设有两头牛A和B都被其他所有牛认为是红人,那么显然,A被B认为是红人,B也被A认为是红人,即存在一个包含A、B两个顶点的圈,或者说,A、B同属于一个强连通分量。反之,如果一头牛被其他所有牛认为是红人,那么其所属的强连通分量内的所有牛都被其他所有牛认为是红人。
由此,我们把图进行强连通分量分解后,至多有一个强连通分量满足题目的条件。
而按前面介绍的算法进行强连通分量分解时,我们还能够得到各个强连通分量拓扑排序后的顺序,[1]唯一可能成为解的只有拓扑序最后的强连通分量。
所以在最后,我们只要检查这个强连通分量是否从所有顶点可达就好了。
该算法的复杂度为O(N+M),足以在时限内解决原题。
以上分析来自挑战程序设计竞赛。
对[1]的理解:
满足条件的强连通分量的特点是:
(1)出度为0
(2)其余的节点都会间接或直接的指向此强连通分量的任一节点
再结合向量vs 的作用,在Dfs( )中,vs中存储的第一个节点肯定是满足条件的强连通分量中的某一节点,在vs中,越靠前的节点的拓扑序越大。
AC代码:
1 #include<iostream> 2 #include<vector> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 const int maxV=1e4+10; 7 const int maxE=5e4+10; 8 9 int N,M; 10 bool vis[maxV]; 11 vector<int >vs; 12 vector<int >edge[maxE],redge[maxE]; 13 14 void addEdge(int u,int v) 15 { 16 edge[u].push_back(v); 17 redge[v].push_back(u); 18 } 19 void Dfs(int u) 20 { 21 vis[u]=true; 22 for(int i=0;i < edge[u].size();++i) 23 { 24 int to=edge[u][i]; 25 if(!vis[to]) 26 Dfs(to); 27 } 28 vs.push_back(u); 29 } 30 void rDfs(int u) 31 { 32 vis[u]=true; 33 for(int i=0;i < redge[u].size();++i) 34 { 35 int to=redge[u][i]; 36 if(!vis[to]) 37 rDfs(to); 38 } 39 } 40 void Scc() 41 { 42 memset(vis,false,sizeof vis); 43 vs.clear(); 44 for(int i=1;i <= N;++i) 45 if(!vis[i]) 46 Dfs(i); 47 48 memset(vis,false,sizeof vis); 49 sameId=0; 50 for(int i=vs.size()-1;i >= 0;--i) 51 { 52 int v=vs[i]; 53 if(!vis[v]) 54 { 55 sameId++; 56 rDfs(v); 57 } 58 } 59 } 60 int Solve() 61 { 62 Scc(); 63 int res=0; 64 int u; 65 for(int i=1;i <= N;++i) 66 if(scc[i] == sameId) 67 { 68 res++;//拓扑序最后的强连通分量的个数 69 u=i;//u == 拓扑排序最后的点 70 } 71 memset(vis,false,sizeof vis); 72 rDfs(u); 73 for(int i=1;i <= N;++i) 74 if(!vis[i])//判断是否全部可达 75 res=0; 76 return res; 77 } 78 int main() 79 { 80 scanf("%d%d",&N,&M); 81 for(int i=1;i <= M;++i) 82 { 83 int u,v; 84 scanf("%d%d",&u,&v); 85 addEdge(u,v); 86 } 87 printf("%d ",Solve()); 88 return 0; 89 }
以上是关于poj 2186(强连通分量入门题)的主要内容,如果未能解决你的问题,请参考以下文章