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提出的求解 ...
随机推荐
- A2. JVM 类加载机制
[概述] 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制. 与那些在编译时需要进行连接 ...
- 迭代器,生成器(generator)和Promise的“微妙”关系
需要Promise源码版的朋友:传送链接 本文主要讲述(iterator)和生成器*/yield之间的联系和各自的用法,以及生成器的高配版本aysnc/await的使用. 大纲: 迭代器(iterat ...
- Opencv下双线性插值法进行图像放缩
关于图像放缩的算法有很多,本文主要介绍双线性插值法进行图像放缩,本文参考了: http://www.cnblogs.com/funny-world/p/3162003.html 我们设源图像src的大 ...
- C++ string 是否以‘\0’结尾 讨论
转载https://blog.csdn.net/qq_31930499/article/details/80374310 之前在某篇文章中看到,C语言字符串是以’\0’结尾的,但是C++string类 ...
- Codeforces Round #530 (Div. 2) (前三题题解)
总评 今天是个上分的好日子,可惜12:30修仙场并没有打... A. Snowball(小模拟) 我上来还以为直接能O(1)算出来没想到还能小于等于0的时候变成0,那么只能小模拟了.从最高的地方进行高 ...
- Re0:DP学习之路 母牛的故事 HDU - 2018
解法 一定要注意斐波那契数列的原始意义,斐波那契数列也叫作兔子数列是兔子繁衍的一种表示方法.同样适用于别的情况的动物繁衍问题 原始的是3个月一胎现在四个月那么方程就是 f(n)=n n<=4 f ...
- php利用32进制实现对id加密解密
前言 最近在项目中遇到一个问题,当前用户分享一个邀请码给好友,好友根据邀请码注册成为新用户之后,则成为当前用户的下级,特定条件下,可以得到下级用户的一系列返利.这里要实现的就是根据当前用户的id,生成 ...
- 51nod 1096 距离之和最小 1108 距离之和最小 V2
[题解] 很显然在一条坐标轴上到各个点距离之和最小的点就是它们的中位数.怎么证明呢?我们假设现在找的某个点x左边有a个点,右边有b个点(a>b).我们把x向左移动d个单位,并保证x左边依然有a个 ...
- codeforces 689 Mike and Shortcuts(最短路)
codeforces 689 Mike and Shortcuts(最短路) 原题 任意两点的距离是序号差,那么相邻点之间建边即可,同时加上题目提供的边 跑一遍dijkstra可得1点到每个点的最短路 ...
- Oracle操作管理之用户和角色
1.用户管理 (1)建立用户(数据库验证) CREATE USER smith IDENTIFIED BY smith_pwd DEFAULTTABLESPACE users TEMPORARY TA ...