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

  • 相关阅读:
    2018-09-13 代码翻译尝试-使用Roaster解析和生成Java源码
    2018-09-10 使用现有在线翻译服务进行代码翻译的体验
    2018-09-06 Java实现英汉词典API初版发布在Maven
    2018-08-29 浏览器插件实现GitHub代码翻译原型演示
    2018-08-27 使用JDT核心库解析JDK源码后初步分析API命名
    2018-08-11 中文代码示例之Spring Boot 2.0.3问好
    2018-08-24 中文代码之Spring Boot对H2数据库简单查询
    2018-08-22 为中文API的简繁转换库添加迟到的持续集成
    2018-08-21 中文关键词替换体验页面原型
    vim打开不同的文件
  • 原文地址:https://www.cnblogs.com/Patt/p/4713459.html
Copyright © 2020-2023  润新知