标签:
有向图的强连通分量
在有向图中,u可达v不一定意味v可达到u,相互可达的节点则属于同一个强连通分量。
某节点的传递闭包为该节点所处的强连通分量和它所有后代所处的强连通分量的节点。
若有向图的所有节点同属于一个强连通分量,则称该有向图为强连通图。
在有向图中,若某子图中的任一对节点都互为可达,则该子图称为有向图的强连通分量。
计算有向图中强连通分量的方法如下:将有向图G中每条边的方向取反,得到图G的一个转置GT,G和GT中的强连通分量相同。将每个强连通分量缩成一个节点,就可以得到一个无环有向图Gscc。
1、Procedure Kosaraju(G);
{调用dfs(G)计算出每个节点的f[u];
计算图G的转置GT;
调用dfs(GT),在主循环中按照f[u]递减的顺序依次对每个未访问点执行dfs过程,则得到的每棵dfs树恰好对应于一个强连通分量;
}
V |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
d |
1 |
17 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
19 |
21 |
23 |
20 |
f |
16 |
18 |
15 |
14 |
13 |
6 |
12 |
11 |
10 |
26 |
22 |
24 |
25 |
f |
26 |
25 |
24 |
22 |
18 |
16 |
15 |
14 |
13 |
12 |
11 |
10 |
6 |
v |
9 |
12 |
11 |
10 |
1 |
0 |
2 |
3 |
4 |
6 |
7 |
8 |
5 |
|
0 |
0 |
0 |
0 |
1 |
2 |
2 |
2 |
2 |
2 |
3 |
3 |
2 |
procedure init;
var i,x,y:integer;
begin
readln(n,m);
for i:=1 to m do
begin
readln(x,y);
inc(map[x,0]);
map[x,map[x,0]]:=y;
inc(map1[y,0]);
map1[y,map1[y,0]]:=x;
end;
end;
procedure dfs(p:integer);
var i,j,k:integer;
begin
visit[p]:=true; k:=map[p,0];
for i:=1 to k do
begin
j:=map[p,i];
if not visit[j] then dfs(j);
end;
inc(pos); list[pos]:=p;
end;
procedure dfs1(p:integer);
var i,j,k:integer;
begin
visit[p]:=true;
k:=map1[p,0];
for i:=1 to k do
begin
j:=map1[p,i];
if not visit[j] then dfs1(j);
end;
end;
procedure kosaraju;
var i,j,k:integer;
begin
fillchar(visit,sizeof(visit),false);
for i:=1 to n do if not visit[i] then dfs(i);
fillchar(visit,sizeof(visit),false); scc:=0;
for i:=pos downto 1 do //每深搜完一次,表示找完一个强连通图,增加scc
if not visit[list[i]] then begin dfs1(list[i]); inc(scc); end;
end;
begin
init;
pos:=0;
kosaraju;
writeln(scc);
end.
2、Tarjan算法
Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
算法流程演示:
1.从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。
2.返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。
3.返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。
4.继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以LOW[2]=DFN[4]=5。返回1后,发现DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。
至此,算法结束。经过该算法,求出了图中全部的三个强连通分量{1,3,4,2},{5},{6}。
可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)。
模板:
var
a:array [1..1000,1..1000] of longint;
low,dfn,c:array [1..1000] of longint;
v,f,ff:array [1..1000] of boolean;
i,j,m,n,x,y,d,ans:longint;
function min(x,y:longint):longint;
begin
if x>y then
exit(y);
exit(x);
end;
procedure tarjan(x:longint);
var
i:longint;
begin
inc(d);
low[x]:=d;
dfn[x]:=d;
f[x]:=true;
for i:=1 to c[x] do
begin
if not v[a[x,i]] then
begin
v[a[x,i]]:=true;
tarjan(a[x,i]);
low[x]:=min(low[x],low[a[x,i]]);
end else
begin
if f[a[x,i]] then
low[x]:=min(low[x],dfn[a[x,i]]);
end;
end;
if dfn[x]=low[x] then
inc(ans);
end;
begin
readln(n);
for i:=1 to n do
begin
read(x);
while x<>0 do
begin
inc(c[i]);
a[i,c[i]]:=x;
read(x);
end;
end;
for i:=1 to n do
if not v[i] then
begin
v[i]:=true;
tarjan(i);
end;
writeln(ans);
end.
有几道习题:
1.http://blog.csdn.net/boyxiejunboy/article/details/46891399
2.http://blog.csdn.net/boyxiejunboy/article/details/46891417
3.http://blog.csdn.net/boyxiejunboy/article/details/46891815
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/boyxiejunboy/article/details/46912203