连通性2 无向图的割边 (cut edge)

这是DFS系列的第二篇

割边的概念

In graph theory, a bridgeisthmuscut-edge, or cut arc is an edge of a graph whose deletion increases its number of connected components. Equivalently, an edge is a bridge if and only if it is not contained in any cycle. A graph is said to be bridgeless or isthmus-free if it contains no bridges.

Let $G = (V, E)$ be a connected, undirected graph, a bridge of $G$ is an edge whose removal disconnects $G$. (Introduction to Algorithms p.621)

注意割边这一概念只适用于无向图,不适用于有向图,因为有向图的连通性和无向图的连通性是完全不同的两个概念。对于某有向图 $G$,简单地称它连通是很不完善的。有向图的连通性有强连通(strongly connected)和半连通(semiconnected)两种常见的提法。上面英文描述中的“graph”及下文中的“”均指无向图

 

割边 (cut edge)也称作(bridge)是删除后能使图的连通分量增加的边。

下面我们只考虑没有重边的无向图

考虑一个连通的无向图 $G$,若它含有某条割边 $(u, v)$,那么去掉这条边后,将得到2个连通图 $G'$,$H'$,而不可能得到 $2$ 个以上连通图,因为一条边最多能将 $2$ 个连通图合为一个联通图。(这句话貌似和上下文无关)

下面介绍求无向图所有割边的Tarjan算法(Tarjan's Bridge-Finding Algorithm

我们只考虑对无向连通图 $G$ 求割边,若图 $G$ 不连通那么就对 $G$ 的各个连通分量求割边。

我们知道 DFS 一个无向图将其所有边分成两类树边(tree edge)与回边(back edge)。显然地,割边只能是树边而绝不可能是回边。

考虑 一条树边 $(u\to v)$ 是割边 的条件。这条件应当是在DFS树中,以 $v$ 为根的子树(简称子树 $v$)中的所有节点都没有连向 $u$ 的祖先节点(包括 $u$ 本身)的回边,也就是说子树 $v$ 仅仅靠着边 $(u,v)$ 和其他节点保持连通。

为了判断上述条件,我们在 DFS 过程中记录每个节点的 dfn 值与 low 值,树边 $(u\to v)$ 是割边的充要条件即是 \(\color{blue}{\mathrm{low}[v]>\mathrm{dfn}[u]}\) 。

 

struct edge{
	int to, nt;
	bool flag;
}E[MAX_E<<1];
int head[MAX_V];

int dfn[MAX_V], low[MAX_V];
int ts;	//time stamp
void dfs(int u, int f){
	dfs[u]=low[u]=++ts;
	for(int i=head[i]; ~i; i=E[i].nt){
		int &v=E[i].to;
		if(!dfn[v]){	//tree edge
			dfs(v, f);
			low[u]=min(low[u], low[v]);
			if(low[v]>dfn[u]){
				e[i].flag=e[i^1].flag=true;
			}
		}
		else if(v!=f&&dfn[v]<dfn[u]){	//back edge
			low[u]=min(low[u], dfn[v]);
		}
	}
}

void solve(int N){
	memset(dfn, 0, sizeof(dfn));
	ts=0;
	for(int i=1; i<=N; i++)
		if(!dfn[i]) dfs(i, i);
}

 现在考虑有重边的情况。这时上面的写法不能识别所有回边。首先明确一点:不论是否有重边,DFS 都将所有边分成树边回边两类。

但是按上面的写法,所有重边要么全是树边,要么全是回边,因而不能识别所有回边(这并不是 DFS 算法本身有问题,而是写法有问题)。这是因为 DFS 的参数是 $u$(当前节点)和 $f$(当前节点的父亲节点),我们判断回边的依据是

 

else if(v!=f&&dfn[v]<dfn[u]){	//back edge
			low[u]=min(low[u], dfn[v]);
		}

解决办法是将参数 $u$换成树边 $( u\to f )$ 的编号。

struct edge{
	int to, nt, id;
	bool tag;
}E[MAX_N<<1];
int head[MAX_N];

int dfn[MAX_N], low[MAX_N], ts;	//time_stamp
void dfs(int u, int te){
	dfn[u]=low[u]=++ts;
	for(int i=head[u]; ~i; i=E[i].nt){
		int &v=E[i].to, &id=E[i].id;
		if(!dfn[v]){	//tree_edge
			dfs(v, id);
			low[u]=min(low[u], low[v]);
			if(low[v]>dfn[u])
				e[i].tag=true;
		}
		else if(id!=te&&dfn[v]<dfn[u]){	//back_edge
			low[u]=min(low[u], dfn[v]);
		}
	}
}

 

一般来说,没必要在结构体 edge 内加个变量 id,按通常的建图方式,无向边的 ID 就是其对应的某条有向边的 ID 右移一位。

posted @ 2015-08-08 16:53  Pat  阅读(4726)  评论(0编辑  收藏  举报