連通性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 右移一位。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM