Tarjan-求强连通分量

知识点-Tarjan

强连通分量:在一个图的子图中,任意两个点相互可达,也就是存在互通的路径,那么这个子图就是强连通分量(或者称为强连通分支)。如果一个有向图的任意两个点相互可达,那么这个图就称为强连通图

当我们实现基于dfs的Tarjan算法时,我们用D[i]记录节点i被访问的时间(别的博客用dfn[i]),用F[i]记录节点i或i的子树最小可以返回到的节点j的D[j](别的博客用low[i])。

让我们模拟一下dfs的过程,每个节点上左边的是D的值,右边的是F的值。

首先从节点1开始往下深搜,每次搜索都更新D值和F值,此时F值是节点本身。

当搜索到节点4时,已经无法继续搜索。而此时D值=F值,所以我们可以判定一个强连通分量(在代码中,需要借助“栈”来寻找强连通分量中所有节点),这个强连通量就是节点4。

从图我们也可以看出,节点4无法到达其它任意节点。

从节点4返回至3,此时节点3也无路可走了。而D值=F值,那么又可以判定一个强连通分量节点3。

从3返回至2,节点2还有一条路可走!深搜节点5,修改其D值和F值。

 

节点5有路通向节点1,发现节点1的值已经被修改过了的!节点5的F值要修改成1,也就是它到达D值最远的节点是1。修改完后无需继续深搜节点1否则会造成死循环!而D值不等于F值,所以返回。

修改节点2的F值后,再返回。

从节点2返回至1,还有一条路通往节点6。深搜节点6并修改6的D值和F值。

节点6有路通向节点5,发现节点5的值已经被修改过!于是修改节点6的F值后返回至节点1。

当返回到节点1时,已经无路可走,D值等于F值,所以又一个强连通分量出来了……

以上的过程都可以用代码来实现。要注意F值的修改:

如果节点i的子节点j的值没有被修改,那么F[i]=min(F[i],F[j]);

否则F[i]=min(F[i],D[j]);

例题

题目描述

求强连通分量……

输入

第一行两个整数,n,m,代表点数及边数。

第2行至m+1行,每行两个整数,a,b,代表有向边的起点和终点。

输出

输出所有强连通分量

样例输入

6 8
1 2
2 3
3 6
5 6
2 5
5 1
1 4
4 5

样例输出

6
3
2 5 4 1

代码

 1 #include<cstdio> 
 2 #include<algorithm> 
 3 using namespace std; 
 4 struct point{ 
 5     int nxt,to; 
 6 }edge[1001]; 
 7 int d[1001],f[1001],stack[1001],head[1001]={0}; 
 8 bool vis[1001]; 
 9 int tol,cnt,n,m,a,b,index; 
10 void add(int u,int v) 
11 { 
12     edge[++cnt].to=v; 
13     edge[cnt].nxt=head[u]; 
14     head[u]=cnt; 
15 } 
16 int tarjan(int node) 
17 { 
18     tol++; 
19     d[node]=f[node]=tol; 
20     stack[++index]=node; 
21     vis[node]=1; 
22     for(int i=head[node];i!=0;i=edge[i].nxt) 
23     { 
24         if(!d[edge[i].to]) 
25         { 
26             tarjan(edge[i].to); 
27             f[node]=min(f[edge[i].to],f[node]); 
28         } 
29         else if(vis[edge[i].to]) 
30         f[node]=min(d[edge[i].to],f[node]); 
31     } 
32     if(d[node]==f[node]) 
33     { 
34         do
35         { 
36             printf("%d",stack[index]); 
37             if(node!=stack[index])printf(" "); 
38             vis[stack[index]]=0; 
39             index--; 
40         }while(node!=stack[index+1]); 
41         printf("\n"); 
42     } 
43 } 
44 int main() 
45 { 
46     scanf("%d%d",&n,&m); 
47     for(int i=1;i<=m;i++) 
48     { 
49         scanf("%d%d",&a,&b); 
50         add(a,b); 
51     } 
52     for(int i=1;i<=n;i++) 
53         if(!d[i])tarjan(i);  
54     return 0; 
55 } 

 

posted on 2017-04-21 13:28  Skype_lorenzo  阅读(289)  评论(0编辑  收藏  举报

导航