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

题目大意:

给定一个含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 }

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

时间: 2024-10-05 12:42:18

POJ 2186 - Popular Cows - 强连通分量,缩点的相关文章

POJ 2186 Popular Cows --强连通分量

题意:给定一个有向图,问有多少个点由任意顶点出发都能达到. 分析:首先,在一个有向无环图中,能被所有点达到点,出度一定是0. 先求出所有的强连通分支,然后把每个强连通分支收缩成一个点,重新建图,这样,这个有向图就变成了一个有向无环图. 在这个新的图中,只需知道出度为0的点有几个即可. 如果出度为0的点超过1个,则输出0:否则输出出度为0的点所代表的那个强连通分支的分量数即可. 用Tarjan求强连通分量 代码: #include <iostream> #include <cstdio&g

POJ 2186 Popular Cows 强连通分量模板

题意 强连通分量,找独立的块 强连通分量裸题 #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <iostream> using namespace std; const int maxn = 50005; int n, m; struct Edge { int v, next;

poj 2186 Popular Cows 强连通缩点

[题意]:给出一群牛中,一头牛认为另一头牛受欢迎的m对关系,关系式可以传递的,即若a认为b受欢迎,b认为c受欢迎,那么a也认为c受欢迎.求最多有多少头牛被所有的牛认为受欢迎. [思路]:把这些关系形成的强连通图缩成点,就形成了一有向无环图,这个图里的出度为0的点(有且只有一个)就是被所有牛认为受欢迎的点,说明若这个点原来是强连通图就要求出这个强连通图里的总点数, 1 #include<iostream> 2 #include<stdio.h> 3 #include<strin

POJ 2186 Popular Cows(Targin缩点)

传送门 Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 31808   Accepted: 12921 Description Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <=

poj - 2186 Popular Cows (强连通)

http://poj.org/problem?id=2186 给定n头牛,m个关系,每个关系a,b表示a认为b是受欢迎的,但是不代表b认为a是受欢迎的,关系之间还有传递性,假如a->b,b->c 则a->c,问有多少头牛被其他所有的牛欢迎. 统计出度为0的点,如果不为1,则表示不存在这样的牛,为1的话就输出这个集合点的数量. 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #

POJ 2186 Popular Cows (强连通分量)

POJ 2186 Popular Cows 链接:http://poj.org/problem?id=2186 题意:每头奶牛都梦想着成为牧群中最受奶牛仰慕的奶牛.在牧群中,有N 头奶牛,1≤N≤10,000,给定M 对(1≤M≤50,000)有序对(A, B),表示A 仰慕B.由于仰慕关系具有传递性,也就是说,如果A 仰慕B,B 仰慕C,则A 也仰慕C,即使在给定的M 对关系中并没有(A, C).你的任务是计算牧群中受每头奶牛仰慕的奶牛数量. 思路:首先可以知道,在同一个强连通分量内的点一定互

强连通分量分解 Kosaraju算法 (poj 2186 Popular Cows)

poj 2186 Popular Cows 题意: 有N头牛, 给出M对关系, 如(1,2)代表1欢迎2, 关系是单向的且可以传递, 即1欢迎2不代表2欢迎1, 但是如果2也欢迎3那么1也欢迎3. 求被所有牛都欢迎的牛的数量. 限制: 1 <= N <= 10000 1 <= M <= 50000 思路: Kosaraju算法, 看缩点后拓扑序的终点有多少头牛, 且要判断是不是所有强连通分量都连向它. Kosaraju算法,分拆完连通分量后,也完成了拓扑序. /*poj 2186

POJ 2186 Popular Cows -- tarjan 缩点

链接: POJ 2186 Popular Cows 题意: 每一头牛都希望在牛群里面备受瞩目,在一个牛群中有N头牛(1<=N<=10000),你被给予M(1<=M<=50000)个关系对,形式如(A,B),这意味着A牛认为B牛比它更受欢迎,由于这种欢迎度是满足传递性的,那么若是A牛认为B牛更受欢迎,B牛认为C牛更受欢迎,那么A牛也会认为C牛更受欢迎.你的任务是计算出被所有牛受欢迎的牛的个数. 输入: 第一行两个整数 N 和 M 第2 到 M + 1 行,两个分开的数 A,B,意味着

poj 2186 Popular Cows 【强连通】

题目:poj 2186 Popular Cows 题意:n头牛,其中存在一些牛相互崇拜,具有传递性,问有多少头牛是被其他所有牛崇拜的. 分析:建立一个有向图,然后强连通缩点,之后求出度为0的点,假如存在多个,那么ans = 0,因为缩点之后如果x崇拜y,x也崇拜z,那么肯定y和z不能互相崇拜,不满足. 然后求出度为0的这个点缩点前环上有多少个点就ans AC代码: #include <cstdio> #include <vector> #include <iostream&g