【DayDayUp】【算法_图_强连通_之一_Kosaraju算法和Tarjan算法】
2017-03-16 10:57
134 查看
【坚持不能偷懒】
譬如一个有向图,V个点,E条边
如果一个区域中的每个点都能到达任意另一个点
则称这个区域强连通
【相对朴素的Kosaraju算法】
需要对图Dfs两发
第一遍Dfs在dfs时记录一个时间(下面栗子里,括号里为时间)
譬如A->B->C B->D这样的图
我搜索A(1) ->搜索B(2) ->搜索C(3)
这时候,发现没了,退出 所以C变成(3,4) 4是退出时间
回到B(2) 继续搜索 -> D(5) ->没了-> D(5,6)
这时候回到B,发现B也没了,那么B(2,7)
回到A(1)
A也没了,变成A(1,8) 结束
所以用一个count记录时间,且Point(in,out)中,只有out是用到的,所以开一个int记录就行
第二遍Dfs
1、把所有边的方向反过来
2、根据 Point(in,out) 中的out最大的点 来dfs,凡是搜索到的,都是小伙伴
3、接着按照剩下out最大的点搜索小伙伴,重复
引用丹神blog的说法,顺毛刷一遍,逆毛刷一遍
复杂度其实也不是很高,两次Dfs
可以认为是 O( 2(M+V) )
【Tarjan算法】
只要dfs一发就行
【Gabow算法】
Tarjan的优化
引用丹神博客上的图
http://blog.csdn.NET/Danliwoo/article/details/51839290
————————PS————————
7 8 1 2 2 3 3 4 4 5 5 6 6 2 5 7 7 1
我以n=7 m=8 对丹神blog的tarjan测试
答案是正确的——一个连通块
但是 序号5的点(low[4])是1 而其余的是 0
(所以,如果要判断每个点的归属的话
可能需要加入并查集来确定?譬如引入Dfs它的来源点作为父节点?)——在退栈时更新Low就行了……
——————————————————
每个点维护两个值(Dfs[i] 和 Low[i])
Dfs[i]用于记录,Dfs到i的时间 用一个count来递增
(而Low[i]表示这个点所能到达的 最低的 Dfs[i]
如果能够维护这两个值,明显同一个连通分量的点
必然他们的Low值,是他们中最小的那个)——这句话好像不是那么准,譬如上面的反例
(用Low[i]维护它是否可达其他早于它的点,因为【维护的栈中】早的点必然可以到达晚的点
能够互相到达,表示连通形成)——好像这样解释更稳点
那怎么维护呢
Dfs,把Dfs到的点【入栈】,同时每个点在Dfs的回溯时更新Low
当Dfs的回溯中,遇到一个【未被改变的点】时
就把栈中的点(到它为止)退栈
同时,可以在退栈时,更新退出的点的low,这样,就能确保连通块的low一致了
把丹神的那个,退栈时加一个更新low的操作,测试下来没问题了。
我写的玩意儿……本着加深自己理解,别人看不看得懂看天意的原则……
所以推荐些更诚恳的讲解——
强连通分支算法--KOSARAJU算法、TARJAN算法和GABOW算法
http://www.cnblogs.com/luweiseu/archive/2012/07/14/2591370.html
(这个Blog上讲的细且好……)
譬如一个有向图,V个点,E条边
如果一个区域中的每个点都能到达任意另一个点
则称这个区域强连通
【相对朴素的Kosaraju算法】
需要对图Dfs两发
第一遍Dfs在dfs时记录一个时间(下面栗子里,括号里为时间)
譬如A->B->C B->D这样的图
我搜索A(1) ->搜索B(2) ->搜索C(3)
这时候,发现没了,退出 所以C变成(3,4) 4是退出时间
回到B(2) 继续搜索 -> D(5) ->没了-> D(5,6)
这时候回到B,发现B也没了,那么B(2,7)
回到A(1)
A也没了,变成A(1,8) 结束
所以用一个count记录时间,且Point(in,out)中,只有out是用到的,所以开一个int记录就行
第二遍Dfs
1、把所有边的方向反过来
2、根据 Point(in,out) 中的out最大的点 来dfs,凡是搜索到的,都是小伙伴
3、接着按照剩下out最大的点搜索小伙伴,重复
引用丹神blog的说法,顺毛刷一遍,逆毛刷一遍
复杂度其实也不是很高,两次Dfs
可以认为是 O( 2(M+V) )
【Tarjan算法】
只要dfs一发就行
【Gabow算法】
Tarjan的优化
引用丹神博客上的图
http://blog.csdn.NET/Danliwoo/article/details/51839290
————————PS————————
7 8 1 2 2 3 3 4 4 5 5 6 6 2 5 7 7 1
我以n=7 m=8 对丹神blog的tarjan测试
答案是正确的——一个连通块
但是 序号5的点(low[4])是1 而其余的是 0
(所以,如果要判断每个点的归属的话
可能需要加入并查集来确定?譬如引入Dfs它的来源点作为父节点?)——在退栈时更新Low就行了……
——————————————————
每个点维护两个值(Dfs[i] 和 Low[i])
Dfs[i]用于记录,Dfs到i的时间 用一个count来递增
(而Low[i]表示这个点所能到达的 最低的 Dfs[i]
如果能够维护这两个值,明显同一个连通分量的点
必然他们的Low值,是他们中最小的那个)——这句话好像不是那么准,譬如上面的反例
(用Low[i]维护它是否可达其他早于它的点,因为【维护的栈中】早的点必然可以到达晚的点
能够互相到达,表示连通形成)——好像这样解释更稳点
那怎么维护呢
Dfs,把Dfs到的点【入栈】,同时每个点在Dfs的回溯时更新Low
当Dfs的回溯中,遇到一个【未被改变的点】时
就把栈中的点(到它为止)退栈
同时,可以在退栈时,更新退出的点的low,这样,就能确保连通块的low一致了
把丹神的那个,退栈时加一个更新low的操作,测试下来没问题了。
#include <cstdio> #include <iostream> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> using namespace std; int n, m, ans; #define N 10100 std::vector<int> v ; int vis , dfn , low , stk , top, ins , clk; void Tarjon(int x){ dfn[x] = low[x] = clk++; stk[top++] = x; vis[x] = ins[x] = 1; for(int j = 0;j < v[x].size();j++){ int y = v[x][j]; if(!vis[y]){ Tarjon(y); low[x] = min(low[x], low[y]); } else if(ins[y]){ low[x] = min(low[x], dfn[y]); } } if(low[x] == dfn[x]){ int sz = 0; do { ins[stk[top-1]] = 0; low[stk[top-1]] = low[x]; top--; sz++; } while(stk[top] != x); if(sz > 1) ans++; } } int main() { while(~scanf("%d%d", &n, &m)){ memset(v, 0, sizeof(v)); for(int i = 0;i < m;i++){ int x, y;scanf("%d%d", &x, &y); x--; y--; v[x].push_back(y); } memset(vis, 0, sizeof(vis)); memset(ins, 0, sizeof(ins)); top = ans = clk = 0; clk = 1; for(int i = 0;i < n;i++) if(!vis[i]) Tarjon(i); for(int i = 0;i<n;i++){ cout<<i<<" "<<dfn[i]<<" "<<low[i]<<endl; } printf("%d\n", ans); } return 0; } /* 7 8 1 2 2 3 3 4 4 5 5 6 6 2 5 7 7 1 */
我写的玩意儿……本着加深自己理解,别人看不看得懂看天意的原则……
所以推荐些更诚恳的讲解——
强连通分支算法--KOSARAJU算法、TARJAN算法和GABOW算法
http://www.cnblogs.com/luweiseu/archive/2012/07/14/2591370.html
(这个Blog上讲的细且好……)
相关文章推荐
- 6.3.1 强连通分支算法--Kosaraju算法、Tarjan算法和Gabow算法
- 6.3.1 强连通分支算法--Kosaraju算法、Tarjan算法和Gabow算法
- Kosaraju算法(发现强连通分图算法)
- 6.3.1 强连通分支算法--Kosaraju算法、Tarjan算法和Gabow算法
- 算法笔记_146:TarJan算法的应用(Java)
- 强连通分支算法--Tarjan算法和Gabow算法
- TC DIV I 500分题目的解答(算法:有向图的强连通)
- CF:Problem 427C - Checkposts强连通 Tarjan算法
- 夜深人静写算法(十)- 有向图强连通和2-sat问题
- 【DayDayUp】【算法_小技巧之一_建图】
- 【转载】Tarjan算法和Kosaraju算法
- 算法摘记 最近公共祖先LCA Tarjan算法
- NYOJ-120 校园网络 &&POJ 1236 (强连通缩点targan算法)
- 强连通分支算法--Kosaraju算法
- 强连通算法--Tarjan个人理解+详解
- 最近公共祖先LCA(Tarjan算法)的思考和算法实现
- 算法-强连通分量和Kosaraju算法
- Kosaraju 算法检测有向图的强连通性
- 【DayDayUp】【算法_图_网络流_之二_Dinic层次图与最小费最大流的概述】(待续)
- 图基本算法介绍:广度优先搜索、深度优先搜索、拓扑排序、强连通分支(算法篇)