POJ 2186 Popular Cows(强连通分量缩点)

题目链接:http://poj.org/problem?id=2186

题目意思大概是:给定N(N<=10000)个点和M(M<=50000)条有向边,求有多少个“受欢迎的点”。所谓的“受欢迎的点”当且仅当任何一个点出发都能到达它。

原来的图是无序且可能有环,用tarjan缩点,变成一个DAG。受欢迎点的出度一定为0,所以缩点后求有多少个连通分量的出度是0,要是大于1则没有受欢迎的点,等于1的话求出这个出度为0的连通分量有多少个点。

强联通分量tarjan算法里邻接表用vector一般好理解,但是速度不及链式前向星,链式前向星学习链接:http://blog.csdn.net/acdreamers/article/details/16902023

AC代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 const int MAXN = 1e4 + 5;
 7 struct data {
 8     int next , to;
 9 }edge[MAXN * 5];
10 int head[MAXN] , low[MAXN] , dfn[MAXN] , block[MAXN] , st[MAXN];
11 int top , ord , sccnum;
12 bool instack[MAXN] , out[MAXN];
13 
14 void init(int n) {
15     for(int i = 1 ; i <= n ; i++) {
16         low[i] = dfn[i] = 0;
17         head[i] = -1;
18         instack[i] = false;
19     }
20     top = ord = sccnum = 0;
21 }
22 
23 void tarjan(int u) {
24     low[u] = dfn[u] = ++ord;
25     st[++top] = u;
26     instack[u] = true;
27     for(int i = head[u] ; ~i ; i = edge[i].next) { //链式前向星
28         int v = edge[i].to;
29         if(!dfn[v]) {
30             tarjan(v);
31             low[u] = min(low[u] , low[v]);
32         }
33         else if(instack[v]) {
34             low[u] = min(low[u] , dfn[v]);
35         }
36     }
37     if(low[u] == dfn[u]) {
38         int v;
39         sccnum++;
40         do {
41             v = st[top--];
42             instack[v] = false;
43             block[v] = sccnum;
44         }while(u != v);
45     }
46 }
47 
48 int main()
49 {
50     int n , m , u , v;
51     while(~scanf("%d %d" , &n , &m)) {
52         init(n);
53         for(int i = 0 ; i < m ; i++) {
54             scanf("%d %d" , &u , &v);
55             edge[i].to = v;     //链式前向星
56             edge[i].next = head[u];
57             head[u] = i;
58         }
59         for(int i = 1 ; i <= n ; i++) {
60             if(!dfn[i])
61                 tarjan(i);
62         }
63         memset(out , false , sizeof(out)); //缩点是否有入度
64         for(int u = 1 ; u <= n ; u++) {
65             for(int i = head[u] ; ~i ; i = edge[i].next) { //链式前向星
66                 int v = edge[i].to;
67                 if(block[v] != block[u])  //不是同一个连通分量
68                     out[block[u]] = true;
69             }
70         }
71         int res = 0 , op = -1;
72         for(int i = 1 ; i <= sccnum ; i++) {
73             if(!out[i]) {  //出度为0的点
74                 res++;
75                 op = i;
76             }
77         }
78         if(res > 1) {
79             printf("0\n");
80         }
81         else {
82             res = 0;
83             for(int i = 1 ; i <= n ; i++) {
84                 if(block[i] == op)
85                     res++;
86             }
87             printf("%d\n" , res);
88         }
89     }
90 }

 

posted @ 2016-03-08 21:09  Recoder  阅读(369)  评论(0编辑  收藏  举报