poj 2186(强连通分量入门题)

Posted violet-acmer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 2186(强连通分量入门题)相关的知识,希望对你有一定的参考价值。

传送门:Problem 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 }
View Code

以上是关于poj 2186(强连通分量入门题)的主要内容,如果未能解决你的问题,请参考以下文章

poj 2186 tarjan求强连通分量

强连通分量+poj2186

[POJ2186]Popular Cows(强连通分量)

POJ 2186:Popular Cows(强连通分量)

POJ2186(强连通分量分解)

POJ 2186 Popular Cows 强连通分量模板