zoukankan      html  css  js  c++  java
  • 连通性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 o v)$ 是割边 的条件。这条件应当是在DFS树中,以 $v$ 为根的子树(简称子树 $v$)中的所有节点都没有连向 $u$ 的祖先节点(包括 $u$ 本身)的回边,也就是说子树 $v$ 仅仅靠着边 $(u,v)$ 和其他节点保持连通。

    为了判断上述条件,我们在 DFS 过程中记录每个节点的 dfn 值与 low 值,树边 $(u o 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 o 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 右移一位。

  • 相关阅读:
    multipart/form-data
    Java面试之SE基础基本数据类型
    数据库中的悲观锁和乐观锁详解
    j2SE基回顾(一)
    Hibernate 检索查询的几种方式(HQL,QBC,本地SQL,集成Spring等)
    消防(bzoj 2282)
    YY的GCD(bzoj 2820)
    Problem b(bzoj 2301)
    完全平方数(bzoj 2440)
    The Luckiest number(hdu 2462)
  • 原文地址:https://www.cnblogs.com/Patt/p/4713459.html
Copyright © 2011-2022 走看看