图的强连通问题

图的强连通问题

——求强连通分量个数,找出每个最大强连通子图。

 

三种算法,Tarjan、Kosaraju、Garbow。先说Tarjan。

 

Tarjan

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<stack>
 6 using namespace std;
 7 const int maxv= 110;
 8 const int maxe= 5010;  //可能的最大值
 9 
10 struct ENode
11 {
12     int to;
13     int Next;
14 };
15 ENode edegs[maxe];
16 int Head[maxv], tnt;
17 void init()
18 {
19     memset(Head, -1, sizeof(Head));
20     tnt= -1;
21 }
22 void Add_ENode (int a, int b)
23 {
24     ++ tnt;
25     edegs[tnt].to= b;
26     edegs[tnt].Next= Head[a];
27     Head[a]= tnt;
28 }
29 
30 int m;
31 int dfn[maxv];  //深度优先搜索中顶点被访问的时间
32 int low[maxv];  //顶点v 和它的邻接点中low[]的最小值
33 int temp[maxv];  //判断节点是否已被访问过(0-未访问 1-已访问未删除 2-已访问已删除)
34 int Stack[maxv];  //手动栈
35 int TarBfs (int v, int lay, int &scc_num)
36 {
37     /*v: 新加入的点; lay: 时间戳; scc_num: 记录强连通分量的数量*/
38 
39     /*第2步:初始化dfn[v]和low[v]*/
40     temp[v]= 1;
41     low[v]= lay;
42     dfn[v]= lay;
43     Stack[++ m]= v;
44     for (int k= Head[v]; k!= -1; k= edegs[k].Next)
45     {
46         /*对于v 的所有邻接节点u:*/
47         int u= edegs[k].to;
48         if (temp[u]== 0)
49         {
50             /*第2-1步:如果没有访问过,则跳转执行第2步,同时维护low[v]*/
51             TarBfs(u, ++ lay, scc_num);
52         }
53         if (temp[u]== 1)
54         {
55             /*第2-2步:如果访问过,但没有删除,维护low[v]*/
56             low[v]= min(low[v], low[u]);
57         }
58     }
59     if (dfn[v]== low[v])
60     {
61         /*如果low[v]== dfn[v],则当前节点是一个强连通分量的根,
62         那么输出相应的强连通分量。*/
63         ++ scc_num;
64         do
65         {
66             low[Stack[m]]= scc_num;
67             temp[Stack[m]]= 2;  //已删除的节点temp更新为2
68         }while (Stack[m --]!= v);
69     }
70     return 0;
71 }
72 
73 int Tarjan(int n)
74 {
75     int scc_num= 0, lay= 1;
76     m= 0;
77     memset(temp, 0, sizeof(temp));
78     memset(low, 0, sizeof(low));
79     for (int i= 1; i<= n; i ++)
80     {
81         if (temp[i]== 0)
82         {
83             /*第1步:找一个没有被访问过的节点v,否则算法结束*/
84             TarBfs(i, lay, scc_num);
85         }
86     }
87     /*返回强连通分量的个数*/
88     return scc_num;
89 }
90 
91 int main()
92 {
93     int n;
94     /*建图*/
95     int ans= Tarjan(n);
96     return 0;
97 }
只求个数

思路:

  如果对于原图进行深度优先搜索,由强连通分量定义可知,任何一个强连通分量是原图的深度优先搜索树的子树。那么,只要确定每个极大强连通子图(强连通分量子树)的根,然后根据这些根从数的最底层开始,一个一个地取出强连通分量即可。

  对于确定强连通分量的根,这里维护两个数组,一个是dfn[ ],一个是low[ ],其中dfn[v ] 表示顶点v 被访问的时间,low[v ]为与顶点v 邻接的未删除的顶点u 的low[u ]与low[v ]的最小值(low[v ]初始化为dfn[v ] )。如果发现一个点,low[v ]== dfn[v ] ,则该点就是一个强连通分量的根。

伪代码:

1.找一个没有被访问过的节点v,否则算法结束

2.初始化dfn[v]和low[v]。对于v 的所有邻接节点u:

①如果没有访问过,则跳转执行第2步,同时维护low[v];

②如果访问过,但没有删除,维护low[v];

如果low[v]== dfn[v],则当前节点是一个强连通分量的根,那么输出相应的强连通分量。

 

posted @ 2019-07-27 10:24  egoist的翻绳游戏  阅读(240)  评论(0编辑  收藏  举报