有向图连通分量SCC

在无向图中,如果从顶点vi到顶点vj有路径,则称vi和vj连通。如果图中任意两个顶点之间都连通,则称该图为连通图,否则,称该图为非连通图,则其中的极大连通子图称为连通分量,这里所谓的极大是指子图中包含的顶点个数极大。
直观地说,极大就是不能再大,或者说再大也不能超过自己。因此,极大连通子图就是:
  设
  1) S为G的子图,S连通,
  2) 如果有S'也是G的连通子图,且S是S'的子图,可推出S = S',
  则称S是G的极大连通子图。
  极小连通子图正好相反,极小就是不能再小,再多小一点就会不连通或点不足。因此,极小连通子图就是:
  设
  1) S为G的子图,S连通,
  2) 如果有S'也是G的连通子图,S'包含G的所有顶点,且S'是S的子图,可推出S' = S,
  则称S是G的级小连通子图。
  注:这个定义和pinejeely给出的等价。这里给出的定义比较容易验证。
在有向图中,如果对于每一对顶点vi和vj,从vi到vj和从vj到vi都有路径,则称该图为强连通图;否则,将其中的极大强连通子图称为强连通分量。
 

Kosaraju算法:先dfs,得到最后完成时间f,再求反图,按f递减的方向对反图再dfs一次。

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <algorithm>
  4 #include <iomanip>
  5 #include <set>
  6 #include <map>
  7 #include <vector>
  8 #include <queue>
  9 using namespace std;
 10 #define N 1000
 11 int head[N], headt[N], cnt;
 12 struct node 
 13 {
 14     int next, to;
 15 }edge[N * 2], edget[N * 2];
 16 
 17 void addedge(int from, int to)//G_T
 18 {
 19     cnt++;
 20     edge[cnt].next = head[from];
 21     edge[cnt].to = to;
 22     head[from] = cnt;
 23     //得到反图
 24     edget[cnt].next = headt[to];
 25     edget[cnt].to = from;
 26     headt[to] = cnt;
 27 }
 28 int f[N];//finishtime
 29 int d[N];//discovertime
 30 int color[N];//init 0 denote white; 1 denote gray discover; 2 denote black finish 
 31 int time; 
 32 int belong[N];//which scc
 33 int cur;//current scc
 34 void dfs_visit(int x)
 35 {
 36     color[x] = 1;
 37     d[x] = ++time;
 38     int i, j;
 39     for (i = head[x]; i; i = edge[i].next)
 40     {
 41         j = edge[i].to;
 42         if (!color[j])
 43         {
 44             dfs_visit(j);
 45         }
 46     }
 47     //color[x] = 2;
 48     f[x] = ++time;
 49 }
 50 
 51 void dfs_visit_t(int x)
 52 {
 53     color[x] = 1;
 54     //d[x] = ++time;
 55     int i, j;
 56     for (i = headt[x]; i; i = edget[i].next)
 57     {
 58         j = edget[i].to;
 59         if (!color[j])
 60         {
 61             dfs_visit_t(j);
 62         }
 63     }
 64     //color[x] = 2;
 65     //f[x] = ++time;
 66     belong[x] = cur;
 67 }
 68 
 69 bool cmp(const int &a, const int &b)
 70 {
 71     return a > b;
 72 }
 73 
 74 map<int, int, greater<int> > mp;//使用map对f[i]进行从大到小排序
 75 map<int, int>::iterator it;
 76 
 77 void init()
 78 {
 79     cnt = cur = 1;
 80     time = -1;
 81     memset(head, 0, sizeof(head));
 82     memset(f, 0, sizeof(f));
 83     memset(d, 0, sizeof(d));
 84     memset(color, 0, sizeof(color));
 85     memset(belong, 0, sizeof(belong));
 86     mp.clear();
 87 }
 88 
 89 int main()
 90 {
 91     int n, m, u, v, i;
 92     while (~scanf("%d%d", &n, &m))
 93     {
 94         init();
 95         for (i = 0; i < m; i++)
 96         {
 97             scanf("%d%d", &u, &v);
 98              addedge(u, v);
 99         } 
100         for (i = 1; i <= n; i++)//得到f
101             if (!color[i])
102                 dfs_visit(i);
103         //sort(f, f + n, cmp);
104         for (i = 1; i <=n; i++)//对f排序
105             mp[f[i]] = i;    
106         memset(color, 0, sizeof(color));
107         for (it = mp.begin(); it != mp.end(); ++it)//对反图进行dfs
108         {
109             if (!color[it->second])
110             {
111                 dfs_visit_t(it->second);
112                 cur++;
113             }        
114         }    
115         for (i = 1; i <=n; i++)
116             printf("%d ", belong[i]);
117     }
118     return 0;
119 }

Tarjan算法只需一次dfs,而且不用求反图,dfs找最早祖先点(最先被发现)也就是寻找B边。

使用LOW函数测试此条件
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 #include <iomanip>
 5 #include <set>
 6 #include <map>
 7 #include <vector>
 8 #include <queue>
 9 #include <stack>
10 #define INF 0x7fffffff
11 using namespace std;
12 #define N 1000
13 
14 stack<int> s;
15 int head[N], cnt;
16 struct node 
17 {
18     int next, to;
19 }edge[N * 2];
20 
21 void addedge(int from, int to)
22 {
23     cnt++;
24     edge[cnt].next = head[from];
25     edge[cnt].to = to;
26     head[from] = cnt;
27 }
28 //int f[N];//finishtime
29 int pre[N];//discovertime 
30 //int color[N];//init 0 denote white; 1 denote gray discover; 2 denote black finish 
31 int time; 
32 int id[N];//which scc
33 int low[N];//
34 int cur;//current scc
35 void dfs_visit(int x)
36 {
37     low[x] = pre[x] = ++time;
38     s.push(x);
39     int i, j;
40     for (i = head[x]; i; i = edge[i].next)
41     {
42         j = edge[i].to;
43         if (!pre[j])
44         {
45             dfs_visit(j);
46         }
47         if (low[j] < low[x])//找最小的low[x],即是否存在后向边(B边)
48             low[x] = low[j];
49     }
50     if (low[x] == pre[x])//找到了一个scc
51     {
52         do
53         {
54             i = s.top();
55             s.pop();
56             low[i] = INF;
57             id[i] = cur;
58         }while (i != x);
59         cur++;
60     }
61 }
62 
63 void init()
64 {
65     cnt = cur = 1;
66     time = 0;
67     memset(head, 0, sizeof(head));
68     memset(pre, 0, sizeof(pre));
69     memset(low, 0, sizeof(low));
70     memset(id, 0, sizeof(id));
71     while (!s.empty())
72         s.pop();
73 }
74 
75 int main()
76 {
77     int n, m, u, v, i;
78     while (~scanf("%d%d", &n, &m))
79     {
80         init();
81         for (i = 0; i < m; i++)
82         {
83             scanf("%d%d", &u, &v);
84              addedge(u, v);
85         }     
86         for (i = 1; i <= n; i++)
87             if (!pre[i])
88                 dfs_visit(i);
89         for (i = 1; i <=n; i++)
90             printf("%d ", id[i]);
91     }
92     return 0;
93 }

Gabow算法:思路与tarjan思路一样,但是用一个stack替代了low数组,使得交换次数减小

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 #include <iomanip>
 5 #include <set>
 6 #include <map>
 7 #include <vector>
 8 #include <queue>
 9 #include <stack>
10 #define INF 0x7fffffff
11 using namespace std;
12 #define N 1000
13 
14 stack<int> s;
15 stack<int> p;
16 int head[N], cnt;
17 struct node 
18 {
19     int next, to;
20 }edge[N * 2];
21 
22 void addedge(int from, int to)
23 {
24     cnt++;
25     edge[cnt].next = head[from];
26     edge[cnt].to = to;
27     head[from] = cnt;
28 }
29 //int f[N];//finishtime
30 int pre[N];//discovertime 
31 //int color[N];//init 0 denote white; 1 denote gray discover; 2 denote black finish 
32 int time; 
33 int id[N];//which scc
34 int low[N];//
35 int cur;//current scc
36 void dfs_visit(int x)
37 {
38     low[x] = pre[x] = ++time;
39     s.push(x);
40     p.push(x);
41     int i, j;
42     for (i = head[x]; i; i = edge[i].next)
43     {
44         j = edge[i].to;
45         if (!pre[j])
46             dfs_visit(j);
47         if (!id[j])//该点未在已求的scc中
48             while (pre[j] < pre[p.top()])存在后向边,出栈
49                 p.pop();    
50     }
51     if (p.top() == x)//找到一个scc
52     {
53         p.pop();
54         do
55         {
56             i = s.top();
57             id[i] = cur;
58             s.pop();
59         }while (i != x);
60         cur++;
61     }
62 }
63 
64 void init()
65 {
66     cnt = cur = 1;
67     time = 0;
68     memset(head, 0, sizeof(head));
69     memset(pre, 0, sizeof(pre));
70     memset(low, 0, sizeof(low));
71     memset(id, 0, sizeof(id));
72     while (!s.empty())
73         s.pop();
74     while (!p.empty())
75         p.pop();
76 }
77 
78 int main()
79 {
80     int n, m, u, v, i;
81     while (~scanf("%d%d", &n, &m))
82     {
83         init();
84         for (i = 0; i < m; i++)
85         {
86             scanf("%d%d", &u, &v);
87              addedge(u, v);
88         }     
89         for (i = 1; i <= n; i++)
90             if (!pre[i])
91                 dfs_visit(i);
92         for (i = 1; i <=n; i++)
93             printf("%d ", id[i]);
94     }
95     return 0;
96 }
原文地址:https://www.cnblogs.com/jecyhw/p/3456759.html