把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

Tarjan在图论中的应用(二)——用Tarjan来求割点与割边

前言:\(Tarjan\)

割点割边建立在 \(Tarjan\)算法的基础之上,因此建议在看这篇博客之前先去学一学\(Tarjan\)

回顾\(Tarjan\)中各个数组的定义

首先,我们来回顾一下\(Tarjan\)中各个数组的定义:

\(dfn[\) \(]\):每个点的\(dfs\)序。

\(low[\) \(]\):每个点能到达的\(dfs\)序最小的节点的\(dfs\)序。

而其他数组在求割点和割边的过程中则不太必要了。

割点

首先,我们要了解一下割点的定义把这个点去掉之后,这个点所在的联通块就会被分成若干个联通块

既然这样,也就是说,只要这个节点某一个子节点所到达的节点的\(dfs\)序大于等于该节点的\(dfs\)序,即它的这个子节点无法到达\(dfs\)序小于该节点的节点,就说明它是一个割点了。

而对于一个联通块第一个访问的节点,则需特判,如果它在遍历完一个节点所能遍历到的所有节点,还能找到没有被遍历过的节点,就说明它是一个割点。

代码如下:

inline void Tarjan(int x,int lst)//Tarjan求割点
{
	register int i,tot=0;//tot记录访问到的子节点个数
	for(dfn[x]=low[x]=++d,i=lnk[x];i;i=e[i].nxt)//枚举每一个子节点
	{
		if(!(e[i].to^lst)) continue;//如果这个节点是当前节点的父亲节点,就跳过
		if(!dfn[e[i].to])//如果这个子节点没有被访问过 
		{
			Tarjan(e[i].to,x),low[x]=min(low[x],low[e[i].to]),++tot;//遍历该子节点,更新low[x],并将tot加1
			if(lst&&low[e[i].to]>=dfn[x]) IsCut[x]=1;//如果当前节点不是一个联通块第一个访问的节点,且当前节点的这个子节点dfs序大于等于当前节点的dfs序,那么就说明当前节点是割点
		}
		else low[x]=min(low[x],dfn[e[i].to]);//更新low[x]
	}
	if(!lst&&tot>=2) IsCut[x]=1;//如果当前节点是联通块第一个节点,且访问到的子节点个数≥2,那么就说明当前节点是割点
}

割边

还是一样,先了解一下割边的定义把这条边去掉之后,这条边所在的联通块就会被分成若干个联通块,看起来与割点的定义很像。

因此,如果一条边所连接的两个节点,若其中\(dfs\)序较大的节点不经过这条边所能到达的\(dfs\)序最小的节点大于这条边连接的点中\(dfs\)序较小的节点,就说明这条边是一条割边。

不过还要注意判重的情况,要注意如果有两条相同的边,那么这两条边肯定都不是割边。

代码如下:

inline void Tarjan(int x,int lst)//Tarjan求割边
{
	register int i,flag=1;//flag记录当前节点是否第一次访问它的父亲节点
	for(dfn[x]=low[x]=++d,i=lnk[x];i;i=e[i].nxt)//枚举每一个子节点
	{
		if(flag&&!(e[i].to^lst)) {flag=0;continue;}//如果是第一次访问父亲节点,就将flag标记为0,并跳过这条边
		if(!dfn[e[i].to])//如果这个子节点没有被访问过
		{
			Tarjan(e[i].to,x),low[x]=min(low[x],low[e[i].to]);//访问这个节点,并更新low[x]
			if(low[e[i].to]>low[x]) IsBridge[i]=1;//如果这条边连向的另一个节点所能到达的dfs序最小的节点大于该节点的dfs序,就说明这条边是割边
		}
		else low[x]=min(low[x],dfn[e[i].to]);//否则,更新low[x]
	}
}
posted @ 2018-10-28 22:28  TheLostWeak  阅读(332)  评论(0编辑  收藏  举报