tarjan算法求强连通分量详解(避免误区)
2017-10-04 10:27
337 查看
trajan算法求强连通分量
这是一个很好的算法OwO我们来自己画个图手模一遍,差不多就可以理解了
网上很多将tarjan的,因为图太简单,特殊情况太少,所以会出现许许多多的误区,希望看了这篇啰里巴嗦的博客能对你有所帮助OwO
我本人一开始死都不明白那个dfn(时间戳)又什么用QAQ后来在fyy大佬%%%的帮助下,发现dfn是个很有用的东西!!!!!!
不多说了,直接上图
输入
15 231 2
4 6
7 13
13 14
10 15
2 3
2 4
5 9
1 8
15 1
8 10
13 3
14 10
3 4
9 4
6 12
3 7
11 3
10 15
8 2
14 3
12 4
4 5
终于拟完一组数据了~~~~
接着开始手模OwO
先初始化,每个点的vis均赋值为0;
接着每个点被访问时均先进栈,访问完毕后出栈。
scc数组记录每个点所从属的强连通分量的编号,初始全为0
从一号点开始dfs,标记dfn[1]=1,low[1]=1,vis[1]=1;
继续dfs,标记dfn[2]=2,low[2]=2,vis[2]=1;
继续dfs,标记dfn[4]=3,low[4]=3,vis[3]=1;
继续dfs,标记dfn[5]=4,low[5]=4,vis[4]=1;
继续dfs,标记dfn[9]=5,low[9]=5,vis[5]=1;
此时栈中元素为:1,2,4,5,9
到这里还是比较和蔼的。。所以一张图解决
然后就出现问题了,4号节点已经被访问(vis[4]=1),要是不做处理的话就会出现死循环。
此时由于scc[4]=0(由于4号点的强连通分量编号还不知道,那么显然它还不属于任何一个强连通分量,于是乎,9->4是一条返祖边而非横叉边),那么我们可以判断出,我们已经找出了一条返祖边,那么我们就不从4号节点搜索了,直接返回,并将low[9]赋值为low[4]
此时dfn[9]!=low[9],即无法构成强连通分量,回溯。
这时候,我们又发现low[5]>low[9](这里面就涉及到dfn的用处了,我本来是打算直接用点的序号来代替dfn的,<似乎还可以在最后处理的时候当并查集来用,其实不然>,但是要是代替了的话,将无法在O(1)的时间内判断出5和9是否在同一个强连通分量里了,再%%%fyy大佬),那么,我们就将low[5]赋值为low[9]因为这很明显,5和9在同一个强连通分量里。但我们此时先不做太大的处理,具体处理方式后面会讲。
另外一棵子树同理。
此时dfn[6]=6,low[6]=6,vis[6]=1;
dfn[12]=7,low[12]=7,vis[12]=1;
栈中元素为:1,2,4,5,9,6,12
回溯到4号点时,我们发现dfn[4]==low[4],那么,我们即可判断栈中4号点之后的所有节点均在同一个强连通分量里(如果存在只有入度,没有出度的点,那么这个点的dfn值一定等于low值,所以改点已经出栈),所以我们将计数器sccn++,并同时进行栈的pop操作,之道把4号点pop出来为止,并且将pop出来的点的scc均附为当前的sccn值。
即:
scc[12]=sccn;12出栈
scc[6]=sccn;6出栈
scc[9]=sccn;9出栈
scc[5]=sccn;5出栈
scc[4]=sccn;4出栈
于是我们得到了第一坨强连通分量{4,5,9,6,12}
继续dfs,dfn[3]=8,low[3]=8,vis[3]=1;
下一个节点是4号(别问我为什么还是4号,我也不知道)
发现vis[4]!=0,并且scc[4]!=0;于是我们就可以知道这是一条爱理不理的横叉边,假装它不存在就行了,继续。
继续dfs,dfn[7]=9,low[7]=9,vis[7]=1;
然后你会发现,7号点马上又回到了3号点,用前面的处理方法,将7号点的low值附为3号点的low值,然而往后分析,你会发现13号,14号,11号节点都有一条指向3号点的返祖边
这种东西是专门卡那些没把tarjan理解透的代码的,也就是我一开始的一个误区:每次找到返祖边就做一遍并查集操作,遇到这种东西,立刻被卡到爆炸,T到飞起,每次都傻傻的取更新一遍
然而真正的tarjan才没那么脆弱,它每次处理时间为O(1),当找到强连通分量时再来更新就快得多,反而减少了不必要的更新次数。
(好像扯得有点远了=。=)(反正我知道你们这段没看懂,别装作看懂了的样子OwO)
此时栈内元素为:1,2,3,7,13,14,11
继续回溯,到3号点时,dfn[3]==low[3],再次出栈操作
sccn++
scc[11]=sccn;11出栈
scc[14]=sccn;14出栈
scc[13]=sccn;13出栈
scc[7]=sccn;7出栈
scc[3]=sccn;3出栈
于是我们得到了强连通分量{3,7,13,14,11}
回溯到2号节点,因为dfn[2]==low[2],所以2是一个强连通分量
sccn++;
scc[2]=sccn;2出栈
回溯到1号节点,
继续dfs,标记dfn[8]=13,low[8]=13,vis[8]=1;
继续dfs,标记dfn[10]=14,low[10]=14,vis[10]=1;
继续dfs,标记dfn[15]=15,low[15]=15,vis[15]=1;
此时栈内元素为:1,8,10,15
继续dfs,又到了1号节点,找到一条15->1的返祖边,将low[15]附为low[1],回溯
又low[10]>low[15]于是low[10]=low[15];
又low[8]>low[10]于是low[8]=low[10];
回溯到1号节点,没有另外的边了,放心,还没完,最后一步出栈操作!!
sccn++
scc[15]=sccn;15出栈
scc[10]=sccn;10出栈
scc[8]=sccn;8出栈
scc[1]=sccn;1出栈
于是我们得到了强连通分量{1,8,10,15}
大功告成!!!!!!!!!!!!!
下面贴代码
#include<bits/stdc++.h> using namespace std; struct Edge{ int w,to,next; }edge[200001]; queue <int> q; stack <int> s; int n,m,i,u,v,w,cnt,num,sccn; int head[200001],low[200001],dfn[200001],scc[200001],d[200001]; bool vis[200001],inq[200001]; void add(int u,int v) { edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } void dfs(int x) { int i; low[x]=dfn[x]=++num;//num必须为全局变量,代表当前访问的节点序号(时间戳) s.push(x); vis[x]=1; for (i=head[x];~i;i=edge[i].next) if (!vis[edge[i].to])//判断是否访问过(排除横叉边和返祖边) { dfs(edge[i].to); low[x]=min(low[x],low[edge[i].to]); } else if (!scc[edge[i].to])//若为返祖边 low[x]=min(low[x],low[edge[i].to]); if (dfn[x]==low[x])//出栈操作 { sccn++; int t; while (1) { t=s.top(); s.pop(); scc[t]=sccn; if (t==x) break; } } } void tarjan() { int i; sccn=num=0; memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(vis,0,sizeof(vis)); memset(scc,0,sizeof(scc)); for (i=1;i<=n;i++)//此处为了防止整个图非联通,就对整个图进行tarjan if (!vis[i]) dfs(i); } int main() { cin >> n >> m; cnt=0; memset(head,-1,sizeof(head)); for (;cnt<m;) { cin >> u >> v; add(u,v); } tarjan(); for (i=1;i<=n;i++) cout << << ' '; }
相关文章推荐
- 【算法及其原理】 Tarjan——求有向图中的强连通分量
- hdu 4685 Prince and Princess 【匈牙利算法-匹配、强连通分量-Tarjan-缩点】
- 强连通算法--Tarjan个人理解+详解
- tarjan算法详解
- Tarjan算法求解强连通分量(SCC)
- 【给将来学神的算法详解--高精】(6)不可避免的时间复杂度
- |算法讨论|强连通分量Tarjan 学习笔记
- 图论——强连通分量:Tarjan算法——练习1
- POJ 1236 tarjan 算法求强连通分量
- LightOJ - 1291 Real Life Traffic (tarjan算法求强连通分量)
- 设计师分析需求的时候需要避免的4个误区详解
- 强连通分量及缩点tarjan算法解析
- tarjan 算法(求强连通分量)
- 算法学习—— tarjan算法求强连通分量 (附带 hdu1827
- 强连通分量(强连通缩点(tarjan))+最小路径覆盖(匈牙利算法)
- 算法模板——Tarjan强连通分量
- Tarjan 算法详解
- hdu 1269 迷宫城堡 tarjan算法求有向图的强连通分量
- POJ 2186 Popular Cows(Tarjan算法求强连通分量)
- 图论算法(6)(更新版) --- Tarjan算法求强连通分量