『Tarjan算法 无向图的割点与割边』
<更新提示>
<第一次更新>
<正文>
无向图的割点与割边
定义:给定无相连通图\(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算法 无向图的割点与割边』的更多相关文章
- 『Tarjan算法 无向图的双联通分量』
无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...
- 『Tarjan算法 有向图的强连通分量』
有向图的强连通分量 定义:在有向图\(G\)中,如果两个顶点\(v_i,v_j\)间\((v_i>v_j)\)有一条从\(v_i\)到\(v_j\)的有向路径,同时还有一条从\(v_j\)到\( ...
- Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)
Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...
- 无向连通图求割点(tarjan算法去掉改割点剩下的联通分量数目)
poj2117 Electricity Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 3603 Accepted: 12 ...
- Tarjan算法 (强联通分量 割点 割边)
变量解释: low 指当前节点在同一强连通分量(或环)能回溯到的dfn最小的节点 dfn 指当前节点是第几个被搜到的节点(时间戳) sta 栈 vis 是否在栈中 ans 指强连通分量的数量 top ...
- 割点 —— Tarjan 算法
由于对于这一块掌握的十分不好,所以在昨天做题的过程中一直困扰着我,好不容易搞懂了,写个小总结吧 qwq~ 割点 概念 在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点 ...
- Tarjan算法求割点
(声明:以下图片来源于网络) Tarjan算法求出割点个数 首先来了解什么是连通图 在图论中,连通图基于连通的概念.在一个无向图 G 中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称 ...
- Tarjan无向图的割点和桥(割边)全网详解&算法笔记&通俗易懂
更好的阅读体验&惊喜&原文链接 感谢@yxc的腿部挂件 大佬,指出本文不够严谨的地方,万分感谢! Tarjan无向图的割点和桥(割边) 导言 在掌握这个算法前,咱们有几个先决条件. [ ...
- Tarjan算法:求解图的割点与桥(割边)
简介: 割边和割点的定义仅限于无向图中.我们可以通过定义以蛮力方式求解出无向图的所有割点和割边,但这样的求解方式效率低.Tarjan提出了一种快速求解的方式,通过一次DFS就求解出图中所有的割点和割边 ...
随机推荐
- LOJ#3043.【ZJOI2019】 线段树 线段树,概率期望
原文链接www.cnblogs.com/zhouzhendong/p/ZJOI2019Day1T2.html 前言 在LOJ交了一下我的代码,发现它比选手机快将近 4 倍. 题解 对于线段树上每一个节 ...
- RabbitMQ原理图
一.RabbitMQ 原理图 二.Rabbit 交换器讲解 1 Direct 交换器(发布与订阅 完全匹配) 1.2搭建环境 1 ...
- pythonpipinstallpymongo报错
1.安装pymongo模块,报错pip版本低,升级版本又报错找不到合适的版本,网友说网络问题,要使用国内的镜像源来加速:pip install pymongo -i http://pypi.douba ...
- webstorm2018.2.3激活
一,简介 WebStorm 是jetbrains公司旗下一款JavaScript 开发工具.目前已经被广大中国JS开发者誉为"Web前端开发神器"."最强大的HTML5编 ...
- Hive参数的临时设置和永久性设置
Hive中有一些参数是系统给提供给用户的,我们可以通过这些参数的设置可以让Hive在不同的模式下工作,或者改变显示的效果. 1.通过set对参数值进行设定,这种设置只能是在本次会话有效,退出Hive就 ...
- python利用xlrd读取excel文件始终报错原因
1.代码按照网上百度的格式进行书写如下: 但运行后,始终报错如下: 百度了xlrd网页: 分明支持xls和xlsx两种格式的文件,但运行始终报错. 最后找到原因是因为我所读取的文件虽然是以.xls命名 ...
- zepto.js-定制zepto步骤
对以上步骤作简单补充 步骤四:在电脑左下角搜索Node.js command prompt 打开这个命令窗口,然后进入zepto-master 即文件存放的位置.也可以直接用cmd进入zepto-ma ...
- 连接Redis_五种数据格式
前面我们已经准备成功开启Redis服务,其端口号为6379,接下来我们就看看如何使用C#语言来操作Redis.就如MongoDB一样,要操作Redis服务,自然就需要下载C#的客户端,这里通过Nuge ...
- Could not resolve placeholder 'IMAGE_SERVER_URL' in string value "${IMAGE_SERVER_URL}"
这种问题 在网上查的是说使用了重复的property-placeholder 可能是在别的xml 也用了property-placeholder 解决方法 加上 ignore-unresolva ...
- Retrofit 实现获取往里圆角图片,且传值到另一个页面
记得加网络权限 java包: // compile 'jp.wasabeef:glide-transformations:3.0.1' implementation 'com.squareup.ret ...