<更新提示>

<第一次更新>


<正文>

无向图的割点与割边

定义:给定无相连通图\(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. UOJ#374. 【ZJOI2018】历史 贪心,LCT

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ374.html 题解 想出正解有点小激动. 不过因为傻逼错误调到自闭.不如贺题 首先我们考虑如何 $O(n ...

  2. selenium操作浏览器的前进和后退

    前进关键字:driver.forward() 后退关键字:driver.back() 测试对象:1.https://www.baidu.com/ 2.https://www.sogou.com/ 实例 ...

  3. Python ftplib模块

    Python ftplib模块 官方文档:https://docs.python.org/3/library/ftplib.html?highlight=ftplib#module-ftplib 实例 ...

  4. 网络编程-SOCKET开发之----3. socket通信工作流程

    1. TCP的socket通信流程 服务端 1)socket----创建socket对象. 2)bind----绑定本机ip+port. 3)listen----监听来电,若在监听到来电,则建立起连接 ...

  5. mongodb4.0支持事务

    事务特性: 原子性:所有的改变都完成一致性:最终执行结果一致就行隔离性:一个事务的执行不能其它事务干扰.持久性:指一个事务一旦提交,数据不会改变,存在数据库中 exports.getSession = ...

  6. elasticsearch简单操作

    现在,启动一个节点和kibana,接下来的一切操作都在kibana中Dev Tools下的Console里完成 创建一篇文档 将小黑的小姨妈的个人信息录入elasticsearch.我们只要输入 PU ...

  7. Java_集合

    定义: 是一种工具,就像是容器,能存储任意数量的具有共同属性的对象. 与数组比较优点: (1)数组定义后长度不可变,集合长度可变: (2)数组只能通过下标访问,且下标类型只能是数字型,而有的集合(ma ...

  8. kibana 创建index pattern 索引模式时过慢导致无法创建成功 以及解决方案

    下面我具体描述一下我遇到的问题. 在kibana上面创建索引点击创建时,一直显示下面的页面 就看到不停的在那转,始终创建不成功. 查看后台日志,看到状态码为403,报了如下的错误 由于我用的是es6版 ...

  9. JAVA---MYSQL 基本知识点 第一部分

     一 :  什么是数据库? 数据库就是数据的仓库,用来存取数据的,也是一个文件系统,但是访问这个这个文件需要通过标准的SQL语句(统一的规则), 二 : 什么是关系型数据库?  关系型数据库通常保存的 ...

  10. centOS7上编译hadoop-2.7.7

    一.阅读编译文档 在hadoop源码包根目录下有个一个BUINDING.txt的文件,文件说明了编译hadoop所需要的一些编译hadoop所需要的一些编译环境相关的东西.不同hadoop版本的要求都 ...