两边DFS求强联通分支,缩点;
算法流程:
dfs(G);
dfs(G') in the order which f[] decreases.
output tree in the dfs forest as a strongly connected component
第一遍DFS为每个顶点打上时间戳,第二遍是反向DFS,按照时间戳递减的顺序访问每个结点,原图中的边作为反向边进行遍历,此时对于强联通分量必定是可以通过其中任一个点访问到,而原图中时间戳较晚(较早访问到的,注意加时间戳的位置)但不属于某个强联通分量的顶点必然到某个强联通分量有一条单向边,反向DFS时由于原边方向改变,无法通过这条边访问到,而时间戳相对较早的,由于按照时间戳递减顺序遍历在访问到它时,此时可以访问到的强联通分量已经访问过了;
缩点的方式:缩点在这里只是个概念,对每个顶点记录下属于那个强联通分量,可以通过两层循环对原图中每对顶点,如果不属于同一个联通支且有一条有向边,说明如果真的缩点之后有一条有向边,注意在两个联通支之间添加多条有向边并不能改变入度和出度为0的联通支的个数。
5WA,反向DFS中的j写做i了,样例竟然通过了,多测试还是有效的。
1 # include <cstdio> 2 # include <cstring> 3 4 # define N 100 + 5 5 6 int n; 7 char g[N][N], vis[N]; 8 int ts, cols; 9 int f[N], c[N], in[N], out[N]; 10 11 int dfs(int i) 12 { 13 vis[i] = 1; 14 for (int j = 1; j <= n; ++j) if (!vis[j] && g[i][j]) 15 { 16 dfs(j); 17 } 18 f[++ts] = i; // 时间戳 19 } 20 21 int rdfs(int i) 22 { 23 vis[i] = 1; 24 c[i] = cols; 25 for (int j = 1; j <= n; ++j) if (!vis[j] && g[j][i]) 26 { 27 rdfs(j); 28 } 29 } 30 31 void solve(void) 32 { 33 // memset(f+1, 0, sizeof(int)*n); 34 memset(vis+1, 0, sizeof(char)*n); 35 ts = 0; 36 for (int i = 1; i <= n; ++i) if (!vis[i]) 37 { 38 dfs(i); 39 } 40 memset(c+1, 0, sizeof(int)*n); 41 memset(vis+1, 0, sizeof(char)*n); 42 cols = 0; 43 44 // for (int i = n; i >= 1; --i) printf("%d\t", f[i]); 45 // putchar('\n'); 46 47 for (int i = n; i >= 1; --i) if (!vis[f[i]]) 48 { 49 ++cols; 50 rdfs(f[i]); 51 } 52 53 if (cols == 1) {printf("0\n"); return ;} 54 memset(out+1, 0, sizeof(int)*cols); 55 memset(in+1, 0, sizeof(int)*cols); 56 for (int i = 1; i <= n; ++i) 57 for (int j = 1; j <= n; ++j) 58 { 59 if (g[i][j] && (c[i]^c[j])) 60 { 61 ++out[c[i]]; 62 ++in[c[j]]; 63 } 64 } 65 int x = 0, y = 0; 66 for (int i = 1; i <= cols; ++i) 67 { 68 if (in[i] == 0) ++x; 69 if (out[i] == 0) ++y; 70 } 71 printf("%d\n", x>y ? x:y); 72 } 73 74 void read_graph(void) 75 { 76 for (int i = 1; i <= n; ++i) 77 { 78 memset(g[i]+1, 0, sizeof(char)*n); 79 int x; 80 while (scanf("%d", &x), x) 81 g[i][x] = 1; 82 } 83 } 84 85 int main() 86 { 87 while (~scanf("%d", &n)) 88 { 89 read_graph(); 90 solve(); 91 } 92 93 return 0; 94 }
第一道强联通分支。