<更新提示>

<第一次更新>


<正文>

无向图的割点与割边

定义:给定无相连通图\(G=(V,E)\)

若对于\(x \in V\),从图中删去节点\(x\)以及所有与\(x\)关联的边后,\(G\)分裂为两个或以上不连通的子图,则称\(x\)为\(G\)的割点。

若对于\(e \in E\),从图中删去边\(e\)之后,\(G\)分裂为两个不连通的子图,则称\(e\)为\(G\)的割边。

对于很多图上问题来说,这两个概念是很重要的。我们将探究如何求解无向图的割点与割边。

预备知识

时间戳

图在深度优先遍历的过程中,按照每一个节点第一次被访问到的顺序给\(N\)个节点\(1-N\)的标记,称为时间戳,记为\(dfn_x\)。

追溯值

设节点\(x\)可以通过搜索树以外的边回到祖先,那么它能回到祖先的最小时间戳称为节点\(x\)的追溯值,记为\(low_x\)。当\(x\)没有除搜索树以外的边时,\(low_x=x\)。


对于追溯值和时间戳,我们可以利用\(dfs\)求解:

  • 将\(low\)和\(dfn\)的初始值赋值为当前访问到的次序
  • 访问当前节点的每一个子节点
  • 若当前子节点的\(dfn\)值为\(0\),递归子节点,并利用子节点的\(low\)值更新当前节点的\(low\)值
  • 若当前子节点的\(dfn\)值非\(0\),则说明这是一条"返祖边",利用该边连接的节点的\(dfn\)值更新当前节点的\(low\)值
  • 对于能不能通过二元环从子节点直接更新父节点,对于割边来说 不能,对于割点来说 无所谓。原因我们等一下详细讨论。

Tarjan 算法

著名的\(Tarjan\)算法可以在线性时间内求解无向图的割点与割边。

我们来了解一下\(Tarjan\)算法。

割点判定法则

存在\(v\)满足\(dfn_u\leq low_v(v \in Son(u))\)时,\(u\)为图的一个割点

特别地,\(u\)为根节点时,需要有两个子节点\(y_1,y_2\)满足要求。

证明:

令\(S\)是从根\(r\)到\(u\)的轨上含\(r\)不含\(u\)的一切点组成的集合,\(T\)是以\(v\)为根的子树上的点集。易知不存在连接\(T\)与\(V-(S∪{u}∪T)\)的边。若存在连接\(t∈T\)与\(s∈S\)的边\(ts\),则它是返祖边,且\(dfn_s<dfn_u\)。这时\(low_v ≤ dfn_s ≤ dfn_u\),与已知\(low_v ≥ dfn_u\)矛盾,故\(ts\)这种边不存在,故\(u\)是割点,证毕。

我的理解:

对于任意的点\(u\),存在\(v\)满足\(dfn_u\leq low_v(v \in Son(u))\)时,就说明\(u\)的子节点无论怎样都无法通过其他边回到\(u\)的父节点,也就是说,\(u\)以后的部分就相当于"孤立"了,删去\(u\),图的联通分量必然增加。

对于之前能否通过二元环更新的问题,验证可知,无论能否更新,\(dfn_u\leq low_v\)都可以满足,原来的割点不会漏判,也不会多判,不受影响。

依此,我们可以用递归的形式实现求解\(dfn\)以及\(low\)数组,并利用判定法则找到割点。

\(Code:\)

inline void Tarjan(int x,int root)
{
dfn[x]=low[x]=++cnt;
int flag=0;
for(int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!dfn[y])
{
Tarjan(y,root);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
flag++;
if(x!=root||flag>1)cutvertex[x]=true;
}
}
else low[x]=min(low[x],dfn[y]);
}
}

割边判定法则

存在\(v\)满足\(dfn_u < low_v(v \in Son(u))\)时,\((u,v)\)为图的一条割边

我们可以用类似于割点判定法则的方法证明割边判断法则,理解也基本相同,这里不再赘述。

值得我们注意的是,在求解割点时,由于判定法则是:

存在\(v\)满足\(dfn_u\leq low_v(v \in Son(u))\)时,\(u\)为图的一个割点

所以我们不在乎程序实现时\(v\)是否会枚举到\(u\)的父亲节点(不会对答案造成影响),但是,我们在求割边时,\(v\)枚举到\(u\)的父亲会带来错误(没有等号,会漏判割边),所以我们利用异或运算和"成对变换"的技巧避免通过无向边回到父亲节点(也称位运算卡掉二元环)。

具体地,如\(2\ xor\ 1=3,3\ xor\ 1=2\),我们将两条有向边组成的无向边存在\(2\),\(3\)两个下标中,得以互相转换。

\(Code:\)

inline void Tarjan(int x,int inedge)
{
dfn[x]=low[x]=++cnt;
for(int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!dfn[y])
{
Tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])
bridge[i]=bridge[i^1]=true;
}
else if(i!=(inedge^1))
low[x]=min(low[x],dfn[y]);
}
}

交换机

Description

n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接。因电子设备容易损坏,需给通讯点配备备用交换机。

但备用 交换机数量有限,不能全部配备,只能给部分重要城市配置。

于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备 用交换机。

请你根据城市线路情况,计算需配备备用交换机的城市个数,及需配备备用交换机城市的编号。

友情提示:图论常见的坑点,重边,自环,还有对本题来说的不连通

Input Format

第一行,一个整数n,表示共有n个城市(2<=n<=20000)

下面有若干行(<=60000):每行2个数a、b,a、b是城市编号,表示a与b之间有直接通讯线路。

Output Format

第一行,1个整数m,表示需m个备用交换机。

下面有m行,每行有一个整数,表示需配备交换机的城市编号。

输出顺序按编号由小到大。如果没有城市需配备备用交换机则输出0。

Sample Input

7
1 2
2 3
2 4
3 4
4 5
4 6
4 7
5 6
6 7

Sample Output

2
2
4

解析

这是一道\(Tarjan\)求割点模板题,借此给出\(Tarjan\)求割点的完整代码。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int N=90000,M=150000;
int n,Last[M],t,cutvertex[N],dfn[N],low[N],cnt,m,ans[N];
struct edge{int ver,next;}e[M];
inline void insert(int x,int y)
{
e[++t].ver=y;e[t].next=Last[x];Last[x]=t;
}
inline void input(void)
{
scanf("%d",&n);
int x,y;
while(~scanf("%d%d",&x,&y))
if(x^y)insert(x,y),insert(y,x);
}
inline void Tarjan(int x,int root)
{
dfn[x]=low[x]=++cnt;
int flag=0;
for(int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!dfn[y])
{
Tarjan(y,root);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
flag++;
if(x!=root||flag>1)cutvertex[x]=true;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
int main(void)
{
input();
for(int i=1;i<=n;i++)
if(!dfn[i])Tarjan(i,i);
for(int i=1;i<=n;i++)
{
if(cutvertex[i])
ans[++m]=i;
}
if(m)printf("%d\n",m);
else printf("0\n");
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}

危险道路

Description

天凯是苏联的总书记。苏联有n个城市,某些城市之间修筑了公路。任意两个城市都可以通过公路直接或者间接到达。 天凯发现有些公路被毁坏之后会造成某两个城市之间无法互相通过公路到达。这样的公路就被称为dangerous pavement。 为了防止美帝国对dangerous pavement进行轰炸,造成某些城市的地面运输中断,天凯决定在所有的dangerous pavement驻扎重兵。可是到底哪些是dangerous pavement呢?你的任务就是找出所有这样的公路。

Input Format

第一行n,m(1<=n<=100000, 1<=m<=300000),分别表示有n个城市,总共m条公路。

以下m行每行两个整数a, b,表示城市a和城市b之间修筑了直接的公路。

Output Format

输出有若干行。每行包含两个数字a,b(a < b),表示 < a,b >是dangerous pavement。请注意:输出时,所有的数对< a,b>必须按照a从小到大排序输出;如果a相同,则根据b从小到大排序。

Sample Input

6 6
1 2
2 3
2 4
3 5
4 5
5 6

Sample Output

1 2
5 6

解析

这是一道\(Tarjan\)求割边模板题,借此给出\(Tarjan\)求割边的完整代码。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int N=100000+200,M=300000+200;
int n,m,t=1,Last[M*2],dfn[N],low[N],bridge[N],cnt,tot;
struct edge{int ver,next;}e[M*2];
pair < int,int > ans[N];
inline void insert(int x,int y)
{
e[++t].ver=y;e[t].next=Last[x];Last[x]=t;
}
inline void input(void)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
insert(x,y);
insert(y,x);
}
}
inline void Tarjan(int x,int inedge)
{
dfn[x]=low[x]=++cnt;
for(int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!dfn[y])
{
Tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])
bridge[i]=bridge[i^1]=true;
}
else if(i!=(inedge^1))
low[x]=min(low[x],dfn[y]);
}
}
int main(void)
{
input();
for(int i=1;i<=n;i++)
if(!dfn[i])Tarjan(i,0);
for(int i=2;i<t;i+=2)
if(bridge[i])
{
if(e[i].ver>e[i^1].ver)swap(e[i].ver,e[i^1].ver);
ans[++tot]=make_pair(e[i].ver,e[i^1].ver);
}
sort(ans+1,ans+tot+1);
for(int i=1;i<=tot;i++)
printf("%d %d\n",ans[i].first,ans[i].second);
return 0;
}

<后记>

『Tarjan算法 无向图的割点与割边』的更多相关文章

  1. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  2. 『Tarjan算法 有向图的强连通分量』

    有向图的强连通分量 定义:在有向图\(G\)中,如果两个顶点\(v_i,v_j\)间\((v_i>v_j)\)有一条从\(v_i\)到\(v_j\)的有向路径,同时还有一条从\(v_j\)到\( ...

  3. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)

    Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...

  4. 无向连通图求割点(tarjan算法去掉改割点剩下的联通分量数目)

    poj2117 Electricity Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 3603   Accepted: 12 ...

  5. Tarjan算法 (强联通分量 割点 割边)

    变量解释: low 指当前节点在同一强连通分量(或环)能回溯到的dfn最小的节点 dfn 指当前节点是第几个被搜到的节点(时间戳) sta 栈 vis 是否在栈中 ans 指强连通分量的数量 top ...

  6. 割点 —— Tarjan 算法

    由于对于这一块掌握的十分不好,所以在昨天做题的过程中一直困扰着我,好不容易搞懂了,写个小总结吧 qwq~ 割点 概念 在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点 ...

  7. Tarjan算法求割点

    (声明:以下图片来源于网络) Tarjan算法求出割点个数 首先来了解什么是连通图 在图论中,连通图基于连通的概念.在一个无向图 G 中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称 ...

  8. Tarjan无向图的割点和桥(割边)全网详解&算法笔记&通俗易懂

    更好的阅读体验&惊喜&原文链接 感谢@yxc的腿部挂件 大佬,指出本文不够严谨的地方,万分感谢! Tarjan无向图的割点和桥(割边) 导言 在掌握这个算法前,咱们有几个先决条件. [ ...

  9. Tarjan算法:求解图的割点与桥(割边)

    简介: 割边和割点的定义仅限于无向图中.我们可以通过定义以蛮力方式求解出无向图的所有割点和割边,但这样的求解方式效率低.Tarjan提出了一种快速求解的方式,通过一次DFS就求解出图中所有的割点和割边 ...

随机推荐

  1. 输出第n个丑数

    方法一:暴力法 代码如下: 判断是否是丑数 public static boolean isUgly(int n){ while(n!=1){ if(n%2 == 0){ n /= 2; }else ...

  2. java基础之IO流及递归理解

    一.IO流(简单理解是input/output流,数据流内存到磁盘或者从磁盘到内存等) 二.File类(就是操作文件和文件夹的) 1.FIleFile类构造方法 注意:通过构造方法创建的file对象是 ...

  3. Sublime2 Package Control不可用修复

    因为教程里用的是sublime2 所以就跟着用了,也没换3,但是最近安装Nodejs做配置时,发现插件安装器不能用了,已点安装就弹窗报错: there are no packages availabl ...

  4. <算法图解>读书笔记:第1章 算法简介

    阅读书籍:[美]Aditya Bhargava◎著 袁国忠◎译.人民邮电出版社.<算法图解> 第1章 算法简介 1.2 二分查找 一般而言,对于包含n个元素的列表,用二分查找最多需要\(l ...

  5. SpringBoot整合多数据源实现

    项目架构 1.导入相关依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifac ...

  6. Java 什么是线程安全

    当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的.其中,正确性指某个类的行 ...

  7. react生命周期函数

      如图,可以把组件生命周期大致分为三个阶段: 第一阶段:是组件第一次绘制阶段,如图中的上面虚线框内,在这里完成了组件的加载和初始化: 第二阶段:是组件在运行和交互阶段,如图中左下角虚线框,这个阶段组 ...

  8. 关于Python2 与 Python3 的区别

    Python是一门动态解释性的强类型定义语言. 1.Python2 : ①.臃肿,源代码的重复量很多.   ②.语法不清晰,掺杂着C,php,Java的一些陋习. Python3 : 几乎是重构后的源 ...

  9. 关于外网无法访问阿里云主机CentOs

    前两天阿里云ECS搞活动,所有买了个三年的Ecs,然后照着之前在虚拟机同样的搭建服务器,一切都很正常,可是 当我配置好防火墙和nginx之后,发现个问题,外网无法访问. 思考: 1.我的nginx没配 ...

  10. [AtCoder3856]Ice Rink Game - 模拟

    Problem Statement An adult game master and N children are playing a game on an ice rink. The game co ...