码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 2186 - Popular Cows - 强连通分量,缩点

时间:2018-02-20 23:35:00      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:数组   nod   建立   size   clu   algo   +=   描述   efi   

题目大意:

给定一个含N个点、M条边的有向图,求其中有多少个点,可以由其他任意一点出发到达它?

N<=1e4,M<=5e4。

为了描述和编程简便,我们建立原图的反图,这样问题转化为:有多少个点满足从它出发可以到达其他任意一点。

若无特殊说明,以下所指的图均为反图

引理1:满足条件的所有点必然在同一强连通分量内。

证明很简单,如果它们不在同一强连通分量内,那么其中必然有两点x,y使得x→y的路径不存在,与题目要求矛盾。

我们考虑求出该图的所有强连通分量,然后对于每个强连通分量,检验从其中任一点出发,能否到达其他所有的点。

我们可以采取缩点的方法,将每个强连通分量缩成一个点。

引理2:缩点后的图必然是一个有向无环图(DAG)。

证明:如果缩点后存在环,那么这个环可以合并成一个更大的强连通分量。这与强连通分量的极大性矛盾。

我们统计缩点后每个点的入度。如果有2个或以上的点入度为0,那么它们是互相不可达的,此时答案为0。

否则,如果只有一个点入度为0,答案就是该点所代表的强连通分量的大小。

实现时可以借助两个数组:sccId[x]表示点x所属的强连通分量的编号,sccSize[x]表示点x所属的强连通分量的大小。

参考:有向图强连通分量的Tarjan算法

代码:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <stack>
  5 
  6 #define FILLARR(arr, ch) memset(arr, ch, sizeof(arr))
  7 
  8 const int maxN = (int)1e4 + 5;
  9 const int maxM = (int)5e4 + 5;
 10 
 11 struct Edge
 12 {
 13     int to, next;
 14     void assign(int t, int n)
 15     {
 16         to = t;
 17         next = n;
 18     }
 19 };
 20 
 21 Edge elist[maxM];
 22 int head[maxN];
 23 int ecnt;
 24 int N, M;
 25 
 26 void initElist()
 27 {
 28     FILLARR(head, -1);
 29     ecnt = 0;
 30 }
 31 
 32 inline void addEdge(int from, int to)
 33 {
 34     elist[ecnt].assign(to, head[from]);
 35     head[from] = (ecnt++);
 36 }
 37 
 38 void input()
 39 {
 40     scanf("%d%d", &N, &M);
 41     initElist();
 42     for (int u, v, i = 0; i < M; i++)
 43     {
 44         scanf("%d%d", &u, &v);
 45         addEdge(v, u);
 46     }
 47 }
 48 
 49 int dfn[maxN];
 50 int low[maxN];
 51 bool inStk[maxN];
 52 int sccId[maxN];
 53 int sccSize[maxN];
 54 int lastDfn = 0;
 55 std::stack<int> stk;
 56 
 57 inline void pushToStk(int v)
 58 {
 59     stk.push(v);
 60     inStk[v] = true;
 61 }
 62 
 63 inline int popFromStk()
 64 {
 65     int v = stk.top();
 66     stk.pop();
 67     inStk[v] = false;
 68     return v;
 69 }
 70 
 71 void dfs(int cur)
 72 {
 73     dfn[cur] = low[cur] = (++lastDfn);
 74     pushToStk(cur);
 75 
 76     for (int e = head[cur]; e != -1; e = elist[e].next)
 77     {
 78         int to = elist[e].to;
 79         if (dfn[to] == 0)
 80         {
 81             dfs(to);
 82             low[cur] = std::min(low[cur], low[to]);
 83         }
 84         else if (inStk[to])
 85             low[cur] = std::min(low[cur], dfn[to]);
 86     }
 87 
 88     if (dfn[cur] == low[cur])
 89     {
 90         for (int v = popFromStk(); ; v = popFromStk())
 91         {
 92             sccId[v] = cur;
 93             sccSize[cur] += 1;
 94             if (v == cur)
 95                 break;
 96         }
 97     }
 98 }
 99 
100 int inDeg[maxN]; //compressed graph in which each SCC is compressed into one node
101 
102 int solve()
103 {
104     for (int i = 1; i <= N; i++)
105         if (dfn[i] == 0)
106             dfs(i);
107 
108     for (int i = 1; i <= N; i++)
109         inDeg[i] = (sccId[i] == i ? 0 : -1);
110 
111     for (int i = 1; i <= N; i++)
112         for (int e = head[i]; e != -1; e = elist[e].next)
113         {
114             int to = elist[e].to;
115             if (sccId[i] != sccId[to])
116                 inDeg[sccId[to]] += 1; //link between two SCCs
117         }
118 
119     int head = (int)(std::find(inDeg + 1, inDeg + N + 1, 0) - inDeg);
120     return std::count(inDeg + head + 1, inDeg + N + 1, 0) > 0 ? 0 : sccSize[head];
121 }
122 
123 int main()
124 {
125     input();
126     printf("%d", solve());
127     return 0;
128 }

 

POJ 2186 - Popular Cows - 强连通分量,缩点

标签:数组   nod   建立   size   clu   algo   +=   描述   efi   

原文地址:https://www.cnblogs.com/Onlynagesha/p/8455973.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!