Tarjan Algorithm
List
Knowledge
基本知识
Tarjan 主要是用来求有向图的强连通分量(缩点)和无向图的桥和割顶。首先都是要求出 DFN 和 LOW 值:DFN 是指一个点被搜索的次序编号,LOW 是指一个点的子树中最小编号。
基本概念
- 割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点。
- 割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。
- 点连通度:最小割点集合中的顶点数。
- 割边(桥):删掉它之后,图必然会分裂为两个或两个以上的子图。
- 割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合。
- 边连通度:一个图的边连通度的定义为,最小割边集合中的边数。
- 缩点:把没有割边的连通子图缩为一个点,此时满足任意两点之间都有两条路径可达。
注:求块<>求缩点。缩点后变成一棵k个点k-1条割边连接成的树。而割点可以存在于多个块中。 - 双连通分量:分为点双连通和边双连通。它的标准定义为:点连通度大于1的图称为点双连通图,边连通度大于1的图称为边双连通图。通俗地讲,满足任意两点之间,能通过两条或两条以上没有任何重复边的路到达的图称为双连通图。无向图G的极大双连通子图称为双连通分量。
复杂度
O(n+m)
有向图
首先搜索一个点,赋 DFN 和 LOW 初值为它的 DFN,把它丢进栈里,然后
遍历它的每个相邻点:
if 如果该点没被搜到过
then 搜索该点 然后用该点的 LOW 更新现在点的 LOW
else if 下个点还在栈里
then 用下个点的 DFN/LOW 更新这个点的 LOW (为什么 DFN 和 LOW 都可以:因为它在栈中说明它还不是强连通分量,所以 LOW 还没被更新)
最后判断一下它的 LOW 值和 DFN 值是否还相等,如果相等,说明有两种情况:
1.它没有出边
2.它的子节点最终回到了它
两种情况都说明以它为根的树为一个强连通分量。这个时候就只要不断退栈到这个点为止,退出来的所有元素为一个强连通分量。
Code
struct Tarjan{
static const int N=500010,M=500010;
int n,m,tot,_clock,scc;
int ne[M],to[M];bool in[N];
int fr[N],low[N],dfn[N],f[N],st[N];
inline void add(int u,int v){
to[++tot]=v;ne[tot]=fr[u];fr[u]=tot;
}
void Init(){
n=gi(),m=gi();
for(int i=1;i<=m;i++){
int u=gi(),v=gi();add(u,v);
}
}
inline void dfs(int x){
dfn[x]=low[x]=++_clock;
st[++tot]=x;in[x]=true;
for(int o=fr[x];o;o=ne[o])
if(!dfn[to[o]])dfs(to[o]),low[x]=min(low[x],low[to[o]]);
else if(in[to[o]])low[x]=min(low[x],low[to[o]]);
if(dfn[x]==low[x]){
scc++;
while(st[tot]!=x){
f[st[tot]]=scc;in[st[tot]]=false;tot--;
}
f[x]=scc;in[x]=false;tot--;
}
}
void Work(){
_clock=tot=scc=0;
memset(dfn,0,sizeof(dfn));
memset(in,false,sizeof(in));
for(int i=1;i<=n;i++)
if(!dfn[i])dfs(i);
}
}Tar;
缩点
上面代码中f[]代表没个点属于那个块,只需把每一块缩成一个点即可,如果两个块中的点有相连,那么将这个块连起来
Code
//在上面结构体中加上这几行就行
vector<int>p[N]
void Rebuild(){
for(int i=1;i<=n;i++)
for(int o=fr[i];o;o=ne[o]){
if(f[i]==f[to[o]])continue;
p[f[i]].push_back(f[to[o]]);
}
}
用途
缩完点非常好,dfs随便跑,有很多方面应用,比如下面Practice的第一题
这个图会变得非常优美,有向无环图→DAG
无向图
在无向图中因为一条边可以来回走,所以要保证不能用父亲更新,但是直接
记父亲又不行,因为可能会有重边,所以就要记录边的编号。更新的过程还是一
样 的 。
Articulation Point-割顶与连通度
在无向连通图中,删除一个顶点v及其相连的边后,原图从一个连通分量变成了两个或多个连通分量,则称顶点v为割点,同时也称关节点(Articulation Point)。一个没有关节点的连通图称为重连通图(biconnected graph)。若在连通图上至少删去k 个顶点才能破坏图的连通性,则称此图的连通度为k。
关节点和重连通图在实际中较多应用。显然,一个表示通信网络的图的连通度越高,其系统越可靠,无论是哪一个站点出现故障或遭到外界破坏,都不影响系统的正常工作;又如,一个航空网若是重连通的,则当某条航线因天气等某种原因关闭时,旅客仍可从别的航线绕道而行;再如,若将大规模的集成电路的关键线路设计成重连通的话,则在某些元件失效的情况下,整个片子的功能不受影响,反之,在战争中,若要摧毁敌方的运输线,仅需破坏其运输网中的关节点即可。
割顶求法
1. 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;
2. 若low[v]>=dfn[u],则u为割点,u和它的子孙形成一个块。因为这说明u的子孙不能够通过其他边到达u的祖先,这样去掉u之后,图必然分裂为两个子图。Analysis:对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。
Code
struct ArticulationPoint{
static const int N=500010,M=500010;
int n,m,tot,_clock;
int ne[M],to[M];bool cut[N];
int fr[N],low[N],dfn[N],father[N];
inline void add(int u,int v){
to[++tot]=v;ne[tot]=fr[u];fr[u]=tot;
}
void Init(){
n=gi(),m=gi();
for(int i=1;i<=m;i++){
int u=gi(),v=gi();add(u,v);add(v,u);
}
}
inline void Tarjan(int x,int fa){
dfn[x]=low[x]=++_clock;father[x]=fa;
for(int o=fr[x];o;o=ne[o])
if(!dfn[to[o]]){
Tarjan(to[o],x);
low[x]=min(low[x],low[to[o]]);
}
else if(to[o]>>1!=fa>>1)low[x]=min(low[x],low[to[o]]);
}
void Work(){
_clock=0;
memset(dfn,0,sizeof(dfn));
for(int i=1;i<=n;i++)
if(!dfn[i])Tarjan(i,-1);
tot=0;int k=0;//这里1为根节点
for(int i=2;i<=n;i++)
if(father[i]==1)tot++;
else if(low[i]>=dfn[father[i]])cut[father[i]]=1,++k;
if(tot>1)cut[1]=true,++k;
printf("%d\n",k);//割顶个数
for(int i=1;i<=n;i++)if(cut[i])printf("%d ",i);
}
}Tar;
Bridge-桥
桥 的 判 定 方 法 是 , 如 果 DFN(u) < LOW(v) 则 边 (u,v) 为 桥 。( 因 为
DFN(u) < LOW(v)说明 v 没有回到 u 之前的节点,即 u 和 v 不是一个块(双连通
分量,顾名思义要有两条路)的。)
Code
struct Bridge{
static const int N=500010,M=500010;
int n,m,tot,_clock,ans;//ans 统计桥的个数
int ne[M],to[M];
int fr[N],low[N],dfn[N];
inline void add(int u,int v){
to[++tot]=v;ne[tot]=fr[u];fr[u]=tot;
}
void Init(){
n=gi(),m=gi();
for(int i=1;i<=m;i++){
int u=gi(),v=gi();add(u,v);add(v,u);
}
}
inline void Tarjan(int x,int fa){
dfn[x]=low[x]=++_clock;
for(int o=fr[x];o;o=ne[o])
if(!dfn[to[o]]){
Tarjan(to[o],x);
low[x]=min(low[x],low[to[o]]);
if(low[to[o]]>dfn[x])ans++;
}
else if(to[o]>>1!=fa>>1)low[x]=min(low[x],low[to[o]]);//重边不为桥
}
void Work(){
_clock=ans=0;
memset(dfn,0,sizeof(dfn));
for(int i=1;i<=n;i++)
if(!dfn[i])Tarjan(i,-1);
printf("%d",ans);
}
}Tar;
一些有用的定理
- 有向无环图中唯一出度为0的点,一定可以由任何点出发均可达(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)
- 有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达。(由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)
Practice
Tarjan Algorithm的更多相关文章
- Code[VS] 1332 题解 【Kosaraju】【Tarjan】
Code[VS] 1332 上白泽慧音题解 Tarjan Algorithm Kosaraju Algorithm 题目传送门:http://codevs.cn/problem/1332/ 题目描 ...
- Tarjan 算法&模板
Tarjan 算法 一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连 ...
- Kosaraju与Tarjan(图的强连通分量)
Kosaraju 这个算法是用来求解图的强连通分量的,这个是图论的一些知识,前段时间没有学,这几天在补坑... 强连通分量: 有向图中,尽可能多的若干顶点组成的子图中,这些顶点都是相互可到达的,则这些 ...
- tarjan算法,一个关于 图的联通性的神奇算法
一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连通(strongly ...
- Tarjan 详解
Tarjan 算法 一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连 ...
- Kosaraju 算法
Kosaraju 算法 一.算法简介 在计算科学中,Kosaraju的算法(又称为–Sharir Kosaraju算法)是一个线性时间(linear time)算法找到的有向图的强连通分量.它利用了一 ...
- BZOJ 1179 Atm 题解
BZOJ 1179 Atm 题解 SPFA Algorithm Tarjan Algorithm Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来 ...
- 【Apio2009】Bzoj1179 Atm
目录 List Description Input Output Sample Input Sample Output HINT Solution Code Dfs 记忆化搜索 Position: h ...
- 迷宫城堡+算法讲解【tarjian算法】
Tarjan 算法 参考博客:https://www.cnblogs.com/shadowland/p/5872257.html 算法讲解 Tarjan 算法一种由Robert Tarjan提出的求解 ...
随机推荐
- Nginx 通过 certbot 为网站自动配置 SSL 证书并续期
一.背景知识 1.1.http 和 https 是什么? 简单来说,http 是一个传输网页内容的协议,比如你看到的 http 开头的网站 http://www.163.com ,其网页上的文字.图片 ...
- top命令的用法
top命令的用法 2018年07月15日 09:50:04 zhuoya_ 阅读数:1858 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/z ...
- css--小白入门篇1
一.引入 css用来描述html,学习css前我们先来学习html的基础标签的用法,再进入css的学习. 本教程面向小白对象,不会讲细枝末节深入的东西. 二.列表 列表有3种 2.1 无序列表 无序列 ...
- CentOS7.x的DNS服务的基础配置
一.bind服务器安装 bind:开源.稳定.应用广泛的DNS服务.bind的软件包名bind,服务名称named. 查看是否安装bind, 安装bind包: rpm -qa bind yum -y ...
- Python关于导入模块的一些感想:
写项目的时候,碰到这种情况 程序业务为core,里面有两个目录,core1 和core2 core1中有三个模块,business main main1 程序入口为bin目录下的project ...
- java导出word的6种方式(转发)
来自: http://www.cnblogs.com/lcngu/p/5247179.html 最近做的项目,需要将一些信息导出到word中.在网上找了好多解决方案,现在将这几天的总结分享一下. 目前 ...
- 一篇入门MongoDB
目录 1.MongoDB 基本介绍 2.MongoDB 基本概念 3.数据库操作 4.集合操作 5.文档操作 6.查询条件 7.索引 1.MongoDB 基本介绍 (1)安装 MongoDB 简单来说 ...
- 关于ajax跨域请求API数据的一些问题
一般来说我们使用jquery的ajax来跨域请求API数据的时候每次请求,就只能请求一组数据,而且当我们再次点击发送ajax请求的时候,新请求的数据会覆盖掉原来的数据,那么如何每次在请求的数据的时候, ...
- 2.Linux文件IO编程
2.1Linux文件IO概述 2.1.0POSIX规范 POSIX:(Portable Operating System Interface)可移植操作系统接口规范. 由IEEE制定,是为了提高UNI ...
- redis学习-简介
1. Redis 简介 1.1 Redis是什么 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统.Red ...