几个定义
桥
桥是特殊的边,一个桥连接两个不同的连通分量,当一条边被去掉的时候,整个图的连通分量个数增加
深度优先数dfn
dfn[]
即 depth first number ,深度优先数,dfn[x] 表示 x 号节点被访问的顺序,也就是dfs的顺序,如图,假设从左上角节点开始dfs,蓝色数字就是对应节点的dfn
直接父节点
如果dfs的其中一次递归有这样的顺序 A->B
那么A是B的直接父节点
祖先
任何满足dfn[v] < dfn[x]
且 v 不是 x 的直接父节点
的节点 v,都是 x 的祖先
子孙
任何满足 dfn[v] > dfn[x]
的节点 v,都是 x 的子孙
最小可达祖先
min_ancestor[]
即 ”最小可达祖先“ ,min_ancestor[x] 表示 x 及其所有子孙)能够到达的,dfn数值最小(即最老,最先被访问)的祖先节点(不包括x的直接父节点)的dfn值
Tarjan算法求解思路
使用Tarjin算法求解桥,需要基于dfs,而在dfs的同时,我们需要维护两个数组
dfn[]
即 depth first number ,深度优先数,dfn[x] 表示 x 号节点被访问的顺序,也就是dfs的顺序min_ancestor[]
即 ”最小可达祖先“ ,min_ancestor[x] 表示 x
及其所有子孙能够到达的,dfn数值最小(即最老,最先被访问的祖先)的节点的dfn值
如果存在一条边 v1 -- v2
,使得 dfn[v1] < min_ancestor[v2]
那么这条边是桥
证明:
dfn表示dfs遍历的顺序,而min_ancestor[v2] > dfn[v1]
表示 v2 的任意子孙,除了通过 v2--v1
这条边,没有其他途径可以到达v1
(因为v2任意子孙可达的最早节点,其dfn都大于dfn[v1],也就是说子孙们最早只能到v2,不能到v1)
伪代码
- 对节点x进行dfs,同时记录x的直接父节点father
- dfn[x] = min_ancestor[x] = 当前dfs进行步数
- 遍历x的所有邻接节点,这些邻接节点暂时叫做child
- 如果child还没被访问,那么他是子孙节点,先对child递归dfs,然后尝试更新x的最小可达祖先,即
min_ancestor[x] = min(min_ancestor[x], min_ancestor[child])
- 如果child已经被访问,且child不是x的直接父节点,那么child是x的祖先,这时候我们发现可以到达祖先,那么记录祖先的dfn值,并且尝试更新x的最小可达祖先,即
min_ancestor[x] = min(min_ancestor[x], dfn[child])
图解
代码
第一行输入顶点数,然后一直输入边 v1 v2 顶点编号(从0开始),直到输入 -1 -1 结束
#