【学习笔记】无向图的割点与桥

声明:本博客所有随笔都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

割点

概念

在一个无向图中,将某一个点以及此点所连的所有的边去掉,剩下的点不再全部连通,那么这个点就叫做割点

tarjan算法

强连通分量中也有个叫 (tarjan) 的算法,因为是由 (color{87CEFF}{同一个人}) 发明出来的,至于这俩算法有没有关联,等后面再谈

首先我们要把整个图变成一颗 (dfs)

(至于为什么要变成一棵树,我想过了但是没有想出来,也许只是为了方便后续操作?如果有知道的小伙伴可以分享一下ヾ(๑╹◡╹)ノ")

随意选择一个点作为根,从根节点往下搜索,每个搜到的节点打个标记,若搜索过程中碰到已打过标记的点则回溯,最后就搜出一颗 (dfs) 树了

例如这样的一张图:

我们选定 (1) 为根节点,做一颗 (dfs) 树,即为:

红色的边即为树边,绿色的边为回边(前向边/返祖边),通过回边可以去往之前到过的点

对于根来说很好判断,只要有两个或以上的子树就为割点,因为去掉之后子树间不可能连通

对于子树中的点,我们记录两个值: (dfn[i])(low[i])(dfn[i]) 表示在 (dfs) 树中的编号,(low[i]) 表示当前点及其子树中所有的节点,通过回边所连的点中的最小编号(貌似有点绕,要多读几遍)

对于一条边 ((u, v)) 来说,如果 (low[v] >= dfn[u]) ,那么 (u) 点即为割点

为什么呢?

因为 (low[v]) 的定义为通过回边连出去的最小编号,则若 (low[v]) 小于 当前 (u) 的编号的话,那么将 (u) 点删掉, (v) 点照样可以通过 (low[v]) 这条边连出去,而形成连通;但若 (low[v]) 所连出去的边最小也还比 (u) 大,意思就是还是在 (u) 的子树中,那么一旦删去了 (u) ,则 (v) 将无法连通,即 (u) 为割点

和强连通分量tarjan的对比

对于搜索到已经走过的点

强连通 (tarjan) 中有一句 low[u] = min(low[u], low[v]);

而割点中的却是 low[u] = min(low[u], dfn[v]);

这其实是从定义上来说的。

割点中若 (v) 是已走过的点,那么 ((u, v)) 一定是回边,即 (u) 只能保证通过此条回边走到 (v) ,不能保证可以直接走到 (low[v]) ,因为可能要经过 (v) , 但如果是往下继续搜就用 low[u] = min(low[u], low[v]) 没有问题,因为能保证 (v)(u) 的子树中。

而强连通分量中,判断条件为 (vis[ver[i]]) ,即是否在栈中,如果在,那 (u)(v) 一定是同一个强联通分量中的,直接用 (low[v]) 更新 (low[u]) 就是合法的了

Code

void tarjan(int u, int fa)//fa只是一个用来判断是否是根节点的变量
{
	dfn[u] = low[u] = ++ num;//dfs树的初值
	int son = 0;
	for(int i = head[u]; i; i = next[i])
	{
		int v = ver[i];
		if(! dfn[v])
		{
			tarjan(v, fa);
			low[u] = min(low[u], low[v]);
			if(u != fa) ans[u] = low[v] >= dfn[u] ? 1 : ans[u];
			else ++ son; //如果是根节点则记录儿子数
		}
		low[u] = min(low[u], dfn[v]);//若 v 是已经到过的点,即 (u, v) 为回边,那么 u 就是可以连到 v 的,即如此更新
	}
	if(u == fa) ans[u] = son >= 2 ? 1 : ans[u];
}

割桥

概念

在一个无向图中,将某条边去掉,剩下的点不再全部连通,那么这条边就叫做割桥

tarjan算法

其实和割点差不多了,若边 ((u, v)) 是桥的话得满足 (low[v] > dfn[u]) ,即 (v) 往上翻最多不能超过 (v) 自己,那样这条边断掉后就不再连通了

Code

我不会说是因为我懒而直接参照上面)

原文地址:https://www.cnblogs.com/Bn_ff/p/12346134.html