转一篇求图割点、连通分量的文章
2011-09-27 12:59
246 查看
我试图把强连通分量,割点,桥 通过一个统一的DFS 融合在一起,主要根据是桥的两端是割点,以及下面的定理。
/*file:SCC.c
在有向图中,如果两个两个节点之间相互可达,则称这两个节点是强连通的Strongly Connected。
定理:每个强连通分量是深度优先搜索树中的一棵子树。
图和它的逆图的SCC相同。
u的传递闭包是u所在的SCC加上这个SCC缩为一个点的后代。
在一个无向连通图中,若把节点v去掉,原图变成不连通的,则称节点v为割点;
若把一条边e去掉,原图变成不连通的,则称边e为桥。
定义DFN(u)为节点u在搜索树中被遍历到的次序编号。
定义LOW(u)为节点u或u的子树能够追溯到的DFN最小的节点。
DFN(u)==LOW(u)<==>以u为根的子树上的所有节点形成一个强连通分量。
无向边(u,v)是桥<==>(u,v)为树枝边,且满足DFN(u)<LOW(v)
一个节点u是割点<==>"(u,v)为树枝边,且满足DFN(u)<=LOW(v)"或u是有两棵子树的根。
DFN(u)==LOW(v)表明u,v之间存在两条以上的边,故自然(u,v)就不是桥了。
无向图的DFS生成树没有横叉边,故其不同子树一定属于不同点连通分量。
根据定义,有
LOW(u) = min of {
DFN(u),
DFN(v),其中(u,v)为后向边(返祖边),这又等价于DFN(v)<DFS(u)且v不是u的父亲节点.
LOW(v),其中(u,v)为树枝边(父子边) }
c -- 使得每个节点只属于一棵DFS生成树。
d,f-- 时间戳:1,..,2|V|, d[u]<f[u], 在d[u]之前u是白色的,在d[u],f[u]之间是灰色的,之后是黑色的。
<u,v>是树枝,若v是第一次被发现;是正向边,若v是u的后裔非树枝边;是反向边,v是u的祖先,环也被认为是反向边;是交叉边,若是其他类型,连接同一/不同树的两个节点。
<u,v>是
树枝或正向边<==> d[u]<d[v]<f[v]<f[u]
反向边<==> d[v]<d[u]<f[u]<f[v] //v为灰色
交叉边<==> d[v]<f[v]<d[u]<f[u] //v为黑色
无向图
sample input1
6
2 3 0
4 0
4 5 0
6 1 0
6 0
0
sample output1
<nothing>
sample input2
4
2 3 0
3 4 0
0
0
sample output2
cut point:2
bridge:<2,4>
有向图
sample input3=sample input1
sample output3
(gdb) p d
$81 = {0, 1, 2, 8, 3, 9, 4, 0 <repeats 121 times>}
(gdb) p l
$82 = {0, 1, 1, 8, 1, 9, 4, 0 <repeats 121 times>}
(gdb) p f
$83 = {0, 12, 7, 11, 6, 10, 5, 0 <repeats 121 times>}
按照f对节点排序:6,4,2,5,3,1
l[6]==d[6]故得一个SCC {6}
l[4]!=d[4],l[2]!=d[2],
l[5]==d[5],故SCC = {5}
l[3]==d[3],故SCC = {1,2,3,4}
程序输出了
cut point:4
bridge:<4,6>
cut point:3
bridge:<3,5>
cut point:1
bridge:<1,3>
这里理解为“u是有向图的割点,若去掉它之后使得某对节点不可达”。例如去掉4,使得2不能到达6;2不是割点,因为去掉它之后图的任意两点可达(虽然不是双向可达)。
类似地,“(u,v)是有向图的桥,若去掉它之后使得某对节点不可达”。例如
去掉桥<3,5>之后,3不能到达5。
应用例子:
割点 1523,1144
桥 3352
SCC 2553
拓扑排序:每次把节点变黑的同时加到链表首部,有向图是DAG当且仅当没有黑边。
#endif
/*file:SCC.c
在有向图中,如果两个两个节点之间相互可达,则称这两个节点是强连通的Strongly Connected。
定理:每个强连通分量是深度优先搜索树中的一棵子树。
图和它的逆图的SCC相同。
u的传递闭包是u所在的SCC加上这个SCC缩为一个点的后代。
在一个无向连通图中,若把节点v去掉,原图变成不连通的,则称节点v为割点;
若把一条边e去掉,原图变成不连通的,则称边e为桥。
定义DFN(u)为节点u在搜索树中被遍历到的次序编号。
定义LOW(u)为节点u或u的子树能够追溯到的DFN最小的节点。
DFN(u)==LOW(u)<==>以u为根的子树上的所有节点形成一个强连通分量。
无向边(u,v)是桥<==>(u,v)为树枝边,且满足DFN(u)<LOW(v)
一个节点u是割点<==>"(u,v)为树枝边,且满足DFN(u)<=LOW(v)"或u是有两棵子树的根。
DFN(u)==LOW(v)表明u,v之间存在两条以上的边,故自然(u,v)就不是桥了。
无向图的DFS生成树没有横叉边,故其不同子树一定属于不同点连通分量。
根据定义,有
LOW(u) = min of {
DFN(u),
DFN(v),其中(u,v)为后向边(返祖边),这又等价于DFN(v)<DFS(u)且v不是u的父亲节点.
LOW(v),其中(u,v)为树枝边(父子边) }
c -- 使得每个节点只属于一棵DFS生成树。
d,f-- 时间戳:1,..,2|V|, d[u]<f[u], 在d[u]之前u是白色的,在d[u],f[u]之间是灰色的,之后是黑色的。
<u,v>是树枝,若v是第一次被发现;是正向边,若v是u的后裔非树枝边;是反向边,v是u的祖先,环也被认为是反向边;是交叉边,若是其他类型,连接同一/不同树的两个节点。
<u,v>是
树枝或正向边<==> d[u]<d[v]<f[v]<f[u]
反向边<==> d[v]<d[u]<f[u]<f[v] //v为灰色
交叉边<==> d[v]<f[v]<d[u]<f[u] //v为黑色
*/ #include<stdio.h> #define N 128 int g ;//对于无向图,g[i][j]==0:无边,1:可重复访问的边,-1:不能访问<j,i> //对于有向图,则按照常规g[i][j]==0 或1表示有无边, int n;//图节点个数。 int d ;//d[i]为开始访问节点i的时间。 int l ;//lOW[i] int p ;//p[i]为节点i的前驱节点。 int c ;//c[i]==0:白色,-1:灰色,1:黑色。//未访问,正访问,已经访问 int b ;//b[i]为节点i的度数。 int time; //下面的f,gc似乎只有演示意义 int f ;//f[i]为结束访问节点i的时间。 int gc ;//g[i][j]==0:无边,1:树枝边,2:反向边,3:正向边,4:交叉边。 void dfs_visit(int u) {//以u为根深度优先搜索图。 int v; c[u] = -1;//此处把u入栈. time++;d[u] = time; l[u] = time; for(v=1;v<=n;v++)if(g[u][v]>0) { if(c[v]==0)//白色,对于有向图,此时一定有d[v]==0 { g[v][u] = -1;//对于有向图应当注释掉 gc[u][v] = 1;//此处<u,v>属于重连通分量(无向图的块) b[u]++; p[v] = u; dfs_visit(v); if(l[v]<l[u])l[u] = l[v]; g[v][u] = 1;//对于有向图应当注释掉 if((p[u]==0 && b[u]>1) || (p[u]>0 && l[v]>=d[u]))printf("cut point:%d\n",u); if(l[v]>d[u])printf("bridge:<%d,%d>\n",u,v); }else if(c[v]<0)//灰色,此时v一定在栈中 { gc[u][v] = 2;//<u,v>为反向边 if(l[v]<l[u])l[u] = l[v]; }else //黑色 { gc[u][v] = d[v] > d[u]? 3:4; } } //此处开始检查 if l[u]==d[u] then 栈中的所有节点形成一个SSC。 //把栈内节点出栈并标记为已经访问 c[u]=1; time++;f[u] = time; } int main() { int u; build(); for(u=1;u<=n;u++)if(c[u]==0) { p[u] = 0; dfs_visit(u); } return 0; } int build() { int i,j; scanf("%d",&n); for(i=1;i<=n;i++) { while(1==scanf("%d",&j) && j) { g[i][j] = 1; g[j][i] = 1;//无向图,对于有向图需注释掉 } } return 0; }#if 0
无向图
sample input1
6
2 3 0
4 0
4 5 0
6 1 0
6 0
0
sample output1
<nothing>
sample input2
4
2 3 0
3 4 0
0
0
sample output2
cut point:2
bridge:<2,4>
有向图
sample input3=sample input1
sample output3
(gdb) p d
$81 = {0, 1, 2, 8, 3, 9, 4, 0 <repeats 121 times>}
(gdb) p l
$82 = {0, 1, 1, 8, 1, 9, 4, 0 <repeats 121 times>}
(gdb) p f
$83 = {0, 12, 7, 11, 6, 10, 5, 0 <repeats 121 times>}
按照f对节点排序:6,4,2,5,3,1
l[6]==d[6]故得一个SCC {6}
l[4]!=d[4],l[2]!=d[2],
l[5]==d[5],故SCC = {5}
l[3]==d[3],故SCC = {1,2,3,4}
程序输出了
cut point:4
bridge:<4,6>
cut point:3
bridge:<3,5>
cut point:1
bridge:<1,3>
这里理解为“u是有向图的割点,若去掉它之后使得某对节点不可达”。例如去掉4,使得2不能到达6;2不是割点,因为去掉它之后图的任意两点可达(虽然不是双向可达)。
类似地,“(u,v)是有向图的桥,若去掉它之后使得某对节点不可达”。例如
去掉桥<3,5>之后,3不能到达5。
应用例子:
割点 1523,1144
桥 3352
SCC 2553
拓扑排序:每次把节点变黑的同时加到链表首部,有向图是DAG当且仅当没有黑边。
#endif
相关文章推荐
- 【POJ2186】Popular Cows 有向图强连通分量详解(再水一篇怕被骂、、所以。。)
- 推荐一篇关于Master Page点滴技巧的好文章
- 一篇感觉不错的文章——解析互联网的“火星思维”
- UML学习的一篇好文章
- Android应用升级构想和要点总结 //转一篇文章,慢慢看
- 对一篇文章计数输出最多的20个(《编程珠玑》)
- 共享单车技术含量,一篇文章全说透了!
- 采集的一篇文章--大流量网站的底层系统架构
- java计算稀疏图连通分量及两点路径、最短路径
- 写一篇与技术无关的文章,如何查询北京住房公积金
- 一篇介绍游戏寻路的好文章
- PHP内核探索:翻译一篇HashTables文章
- 一篇介绍VC开发OCX的文章,写得太棒了
- 一篇文章告诉你,该学R还是Python
- [CTO札记]一篇精彩的分析文章,证明平台化战略的价值
- POJ-1904 King's Quest 强连通分量求完美匹配
- 一篇五年码农的非结构化文章
- UVA 11324 有向图强连通分量缩点得SCC图,并在其上求最长路径
- 推荐一篇用Blog开发企业信息系统的文章
- 关于IE hasLayout的一篇经典好文章