题意:
给定一个有向图,求有多少个顶点是由任何顶点出发都可达的。
顶点数<= 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。