poj2186 Popular Cows

题意:

给定一个有向图,求有多少个顶点是由任何顶点出发都可达的。
顶点数<= 10,000,边数 <= 50,000

思路:

1. Korasaju算法把图进行强连通分量分解,在分解的同时得到各个强连通分量拓扑序。唯一可能成为解的就是拓扑序最后的强连通分量,最后再检查这个强连通分量是否能从各个顶点均可达即可。

2. Tarjan算法。

实现:

1.

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <vector>
 4 #include <cstring>
 5 using namespace std;
 6 
 7 vector<int> G[10005], G_t[10005], res;
 8 int n, m, x, y, cmp[10005];
 9 bool vis[10005];
10 void dfs(int x)
11 {
12     vis[x] = true;
13     for (int i = 0; i < G[x].size(); i++)
14     {
15         if (!vis[G[x][i]])
16             dfs(G[x][i]);
17     }
18     res.push_back(x);
19 }
20 
21 void rdfs(int x, int k)
22 {
23     vis[x] = true;
24     cmp[x] = k;
25     for (int i = 0; i < G_t[x].size(); i++)
26     {
27         if (!vis[G_t[x][i]])
28         {
29             rdfs(G_t[x][i], k);
30         }
31     }
32 }
33 
34 int main()
35 {
36     while (cin >> n >> m)
37     {
38         for (int i = 1; i <= n; i++)
39         {
40             G[i].clear();
41             G_t[i].clear();
42         }
43         res.clear();
44         for (int i = 0; i < m; i++)
45         {
46             scanf("%d %d", &x, &y);
47             G[x].push_back(y);
48             G_t[y].push_back(x);
49         }
50         memset(vis, 0, sizeof(vis));
51         for (int i = 1; i <= n; i++)
52         {
53             if (!vis[i])
54                 dfs(i);
55         }
56         memset(vis, 0, sizeof(vis));
57         int k = 0;
58         for (int i = res.size() - 1; i >= 0; i--)
59         {
60             if (!vis[res[i]])
61             {
62                 rdfs(res[i], k++);
63             }
64         }
65         int cnt = 0, tmp = 0;
66         for (int i = 1; i <= n; i++)
67         {
68             if (cmp[i] == k - 1)
69             {
70                 cnt++;
71                 tmp = i;
72             }
73         }
74         memset(vis, 0, sizeof(vis));
75         rdfs(tmp, 0);
76         for (int i = 1; i <= n; i++)
77         {
78             if (!vis[i])
79             {
80                 cnt = 0;
81                 break;
82             }
83         }
84         printf("%d
", cnt);
85     }
86     return 0;
87 }

2.

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <vector>
  4 #include <cstring>
  5 #include <stack>
  6 #include <algorithm>
  7 using namespace std;
  8 
  9 vector<int> G[10005];
 10 int index = 1, now = 1, low[10005], dfn[10005], in[10005], cmp[10005], out[10005];
 11 int n, m, x, y;
 12 stack<int> s;
 13 
 14 void tarjan(int x)
 15 {
 16     dfn[x] = low[x] = index++;
 17     s.push(x);
 18     in[x] = true;
 19     for (int i = 0; i < G[x].size(); i++)
 20     {
 21         int v = G[x][i];
 22         if (!dfn[v])
 23         {
 24             tarjan(v);
 25             low[x] = min(low[x], low[v]);
 26         }
 27         else if (in[v])
 28         {
 29             low[x] = min(low[x], dfn[v]);
 30         }
 31     }
 32     if (low[x] == dfn[x])
 33     {
 34         while (s.top() != x)
 35         {
 36             cmp[s.top()] = now;
 37             in[s.top()] = false;
 38             s.pop();
 39         }
 40         cmp[s.top()] = now++;
 41         in[s.top()] = false;
 42         s.pop();
 43     }
 44 }
 45 
 46 int main()
 47 {
 48     while (cin >> n >> m)
 49     {
 50         for (int i = 1; i <= n; i++)
 51         {
 52             G[i].clear();
 53         }
 54         memset(cmp, 0, sizeof(cmp));
 55         memset(out, 0, sizeof(out));
 56         memset(low, 0, sizeof(low));
 57         memset(dfn, 0, sizeof(dfn));
 58         memset(in, 0, sizeof(in));
 59         index = now = 1;
 60         while (!s.empty())
 61         {
 62             s.pop();
 63         }
 64         for (int i = 0; i < m; i++)
 65         {
 66             scanf("%d %d", &x, &y);
 67             G[x].push_back(y);
 68         }
 69         for (int i = 1; i <= n; i++)
 70         {
 71             if (!dfn[i])
 72             {
 73                 tarjan(i);
 74             }
 75         }
 76         int cnt = 0;
 77         for (int i = 1; i <= n; i++)
 78         {
 79             if (cmp[i] == 1)
 80                 cnt++;
 81         }
 82         for (int i = 1; i <= n; i++)
 83         {
 84             for (int j = 0; j < G[i].size(); j++)
 85             {
 86                 int v = G[i][j];
 87                 if (cmp[i] != cmp[v])
 88                 {
 89                     out[cmp[i]]++;
 90                 }
 91             }
 92         }
 93         int num = 0;
 94         for (int i = 1; i < now; i++)
 95         {
 96             if (!out[i])
 97             {
 98                 if (!num)
 99                     num++;
100                 else
101                 {
102                     cnt = 0;
103                     break;
104                 }
105             }
106         }
107         printf("%d
", cnt);
108     }
109     return 0;
110 }

总结:

有向图进行进行强连通分量分解并缩点之后可以得到一个DAG。

如果有多于一个出度为0的缩点则解为0。

原文地址:https://www.cnblogs.com/wangyiming/p/6357988.html