RobertTarjan真的是一个传说级的大人物。

他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决。

而且,Tarjan并不只发明了LCT,他对计算机科学做出的贡献真的很多。

这一篇我就来以他名字命名的Tarjan算法可以O(n)求出无向图的割点和桥。

进一步可以求出无向图的DCC( 双连通分量 )。不止无向图,Tarjan算法还可以求出有向图的SCC( 强连通分量 )。

Tarjan算法基于dfs,接下来我们引入几个基本概念。

dfn:时间戳

我们对一张图进行深度优先遍历,根据第一次访问到它的时间顺序给它打上一个标记,这个标记就是时间戳。

搜索树:

在一张无向连通图中选定任意一个节点进行深度优先遍历,每个点仅访问一次。所有发生了递归的边会构成一棵树,我们称其为无向连通图的“搜索树”。

追溯值:

除了时间戳,Tarjan算法还引入了另一个概念:“追溯值” low。

我们用subtree(x)表示搜索树中以x为根的子树,low[x]定义为下列节点的时间戳的最小值:

1. subtree(x)中的节点 2. 通过一条不在搜索树上的边,能够到达subtree(x)中的节点

我们来画一个图理解一下:(方便起见,图中的节点编号就是它的时间戳)

图中红色的边就是这张图的搜索树

那么我们容易得出:subtree(2)={4,3},5可以通过不在搜索树上的边到达subtree(2)。

所以,low[2]=min{dfn[4],dfn[3],dfn[5]},得出low[2]=3。

根据定义来计算low[x]的方法就非常明显了。因为subtree(x)包括x,所以先令low[x]=dfn[x]。

然后遍历从x出发的每一条边(x,y),计算low[x]。

接下来给出无向图的桥和割点判定法则。

无向边(x,y)是桥,当且仅当x在搜索树上的一个子节点y满足low[y]>dfn[x]。

若x不是搜索树的根节点,则x是割点当且仅当搜索树上的一个子节点y满足low[y]>=dfn[x]。

若x是根节点,则x是割点当且仅当搜索树上存在至少两个x的子节点y1,y2满足上式。

桥边有以下性质:

1. 桥一定是搜索树中的边 2. 一个简单环中边都不是桥边

一个环被称为简单环当且仅当其包含的所有点都只在这个环中被经过了一次。

扩展内容:这里给出用dfs求出一个图中所有简单环的代码

int cnt;
void dfs(int u){
dfn[u]=++cnt;
for(int i=head[u];i;i=nxt(i)){
int v=to(i);
if(v==fa[u])continue;
if(!dfn[v])fa[v]=u,dfs(v);
else if(dfn[u]<dfn[v]){
printf("%d",v);
do{
printf(" %d",fa[v]);v=fa[v];
}while(v!=u);
//找完一个环了
putchar('\n');
}
}
}

这个作为扩展内容就不再展开叙述。

下面给出求出无向图中所有的桥的代码:

#include<bits/stdc++.h>
#define N 100010
using namespace std;
inline int read(){
int data=,w=;char ch=;
while(ch!='-' && (ch<''||ch>''))ch=getchar();
if(ch=='-')w=-,ch=getchar();
while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
return data*w;
}
struct Edge{
int nxt,to;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
}e[N<<];
int dfn[N],low[N],tot=;//储存边的编号,由于要用^1找反向边,从1开始
int bridge[N],head[N];
int n,m,cnt;
void addedge(int f,int t){
nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
}
void tarjan(int x,int in_edge){//in_edge表示递归进入每个节点的边的编号
dfn[x]=low[x]=++cnt;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);//在搜索树上的边
if(low[y]>dfn[x])//桥判定法则
bridge[i]=bridge[i^]=;//这条边和它的反向边都是桥
}else if(i!=(in_edge^))
low[x]=min(low[x],dfn[y]);//不在搜索树上的边
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;i++){
int x=read(),y=read();
addedge(x,y);addedge(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])tarjan(i,);
for(int i=;i<tot;i+=)
if(bridge[i])
printf("%d %d\n",to(i^),to(i));//输出桥两边的点
}

以上就是求无向图中所有桥的程序了,可以自己画图模拟一下tarjan算法的流程加深理解。

下面给出求无向图中所有割点的程序:

这里需要注意的是,由于割点判定法则是小于等于号,所以不需要考虑父节点和重边的问题,所有dfn[x]都可以用来更新low[x]

#include<bits/stdc++.h>
#define N 100010
using namespace std;
inline int read(){
int data=,w=;char ch=;
while(ch!='-' && (ch<''||ch>''))ch=getchar();
if(ch=='-')w=-,ch=getchar();
while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
return data*w;
}
struct Edge{
int nxt,to;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
}e[N<<];
int head[N],dfn[N],low[N],rt,tot=,n,m,cnt;
int cut[N];
inline void addedge(int f,int t){
nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
}
void tarjan(int x){
dfn[x]=low[x]=++cnt;
int flag=;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){//就一个割点判断法则没必要解释什么了吧?
flag++;
if(x!=rt||flag>)cut[x]=;
}
}else low[x]=min(low[x],dfn[y]);
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;i++){
int x=read(),y=read();
if(x==y)continue; //自环直接判掉好吧,不多bb
addedge(x,y);addedge(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])rt=i,tarjan(i);//无向图不一定连通,对每一个连通块都要跑一发tarjan
for(int i=;i<=n;i++)
if(cut[i])printf("%d ",i);
return ;
}

桥边判定法则和割点判定法则后面会update上,这一篇暂时更到这里,下一篇讲e-DCC和v-DCC的求法

主要是联赛在即,更新尽量多的算法扎实自己基础才要紧些...请多见谅

[Tarjan系列] Tarjan算法求无向图的桥和割点的更多相关文章

  1. tarjan算法求无向图的桥、边双连通分量并缩点

    // tarjan算法求无向图的桥.边双连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> ...

  2. Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

     原题链接   无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍 ...

  3. Hdu 4738【tanjan求无向图的桥】割边判定定理 dfn[x] < low[y]

    题目: 曹操在长江上建立了一些点,点之间有一些边连着.如果这些点构成的无向图变成了连通图,那么曹操就无敌了.刘备为了防止曹操变得无敌,就打算去摧毁连接曹操的点的桥.但是诸葛亮把所有炸弹都带走了,只留下 ...

  4. [Tarjan系列] Tarjan算法求无向图的双连通分量

    这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量. 双联通分量包括点双连通分量v-DCC和边连通分量e-DCC. 若一张无向连通图不存在割点,则称它为 ...

  5. tarjan算法--求无向图的割点和桥

    一.基本概念 1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 2.割点:无向连通图中 ...

  6. hdu 4738 Caocao's Bridges 求无向图的桥【Tarjan】

    <题目链接> 题目大意: 曹操在长江上建立了一些点,点之间有一些边连着.如果这些点构成的无向图变成了连通图,那么曹操就无敌了.周瑜为了防止曹操变得无敌,就打算去摧毁连接曹操的点的桥.但是诸 ...

  7. [Tarjan系列] Tarjan算法与有向图的SCC

    前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点. 本篇文章资料参考:李煜东<算法竞赛进阶指南> 这一篇我们讲如何用Tarjan算法求有向图的SCC( ...

  8. 蓝桥杯历届试题 危险系数(dfs或者并查集求无向图关于两点的割点个数)

    Description 抗日战争时期,冀中平原的地道战曾发挥重要作用. 地道的多个站点间有通道连接,形成了庞大的网络.但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系. 我们来定义一个 ...

  9. SPF Tarjan算法求无向图割点(关节点)入门题

    SPF 题目抽象,给出一个连通图的一些边,求关节点.以及每个关节点分出的连通分量的个数 邻接矩阵只要16ms,而邻接表却要32ms,  花费了大量的时间在加边上. //   time  16ms 1 ...

随机推荐

  1. 冲刺阶段——Day1

    [成员分工及任务量] 成员 分工 任务量(小时) 王梓鸿 完成页面设计并编写输入输出图形界面,部分代码测试 20 童皓祯 编写注册和登录模块代码,部分代码测试 20 林郅聪 绘制燃尽图,程序功能整合及 ...

  2. Android5.0以下drawable tag vector错误的解决办法(转发)

    Android5.0以下drawable tag vector错误的解决办法 在Androi 5.0以下的设备可能会报这样的错误: Caused by: org.xmlpull.v1.XmlPullP ...

  3. selenium 隐式等待报错 value must be a non-negative integer

    笔者运行代码使用selenium的隐式等待时出现报错: from selenium import webdriver # 从selenium导入webdriver import time driver ...

  4. 会声会影x7 每次安装均会提示:已安装这个产品的另一个版本

    会声会影x7 每次安装均会提示:已安装这个产品的另一个版本 卸载C++2008 的库就行了 文章来源:刘俊涛的博客 欢迎关注,有问题一起学习欢迎留言.评论

  5. UML期末复习题——2.2:UML Activity Diagram.

    第二题:活动图 重要概念: 活动图:一种有助于使工作流和业务过程可视化的图. 绘制要点: 具体方法见:http://www.cnblogs.com/xiaolongbao-lzh/p/4591953. ...

  6. Flask-分开Models解决循环引用

    Flask-分开Models解决循环引用 在之前我们测试中,所有语句都在同一个文件中,但随着项目越来越大,管理起来有所不便,所以将Models分离.基本的文件结构如下 \—–app.py\—–mode ...

  7. xcode报错: 找不到路径或者资源错误:no such file or directory

    报错截图: 出现的问题: 运行项目页面图片不显示. 解决方法: 1>[COMMAND+shift+G],前往文件夹,输入:  ~/Library/Developer/Xcode/DerivedD ...

  8. Maven项目添加阿里云HBase依赖之后第一行才出现红叉报错“Missing artifact jdk.tools:jdk.tools:jar:1.6”的解决办法

    首先是从阿里云上下载了一个样例项目"hbase-demo",然后用eclipse打开,此时eclipse会去下载aliyun.hbase依赖. 等待一段时间后,pom.xml却报错 ...

  9. 集群架构01.Nginx初步安装配置

    1.切换163yum 源,环境介绍 [root@moban ~]# cat /etc/redhat-release CentOS release 6.5 (Final) mv CentOS-Base. ...

  10. 字符编码笔记:ASCII,Unicode 和 UTF-8(理解)

    1.ASCII 码 美国制定的字符编码规则,对英语字符与二进制位之间的关系做了统一规定. 占一个字节,8 位,最多可表示 2^8 = 256 种状态(字符) 实际共有 128 个字符,只占用一个字节的 ...