List

Knowledge

基本知识

Tarjan 主要是用来求有向图的强连通分量(缩点)和无向图的桥和割顶。首先都是要求出 DFN 和 LOW 值:DFN 是指一个点被搜索的次序编号,LOW 是指一个点的子树中最小编号。

基本概念

  1. 割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点。
  2. 割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。
  3. 点连通度:最小割点集合中的顶点数。
  4. 割边(桥):删掉它之后,图必然会分裂为两个或两个以上的子图。
  5. 割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合。
  6. 边连通度:一个图的边连通度的定义为,最小割边集合中的边数。
  7. 缩点:把没有割边的连通子图缩为一个点,此时满足任意两点之间都有两条路径可达。
    注:求块<>求缩点。缩点后变成一棵k个点k-1条割边连接成的树。而割点可以存在于多个块中。
  8. 双连通分量:分为点双连通和边双连通。它的标准定义为:点连通度大于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;

  

一些有用的定理

  1. 有向无环图中唯一出度为0的点,一定可以由任何点出发均可达(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)
  2. 有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达。(由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)

Practice

CSDN-Bzoj1179 Apio2009 Atm(2016-09-20 19:54)

Cnblogs-Bzoj1179 Apio2009 Atm(2016-09-20 19:54)

Tarjan Algorithm的更多相关文章

  1. Code[VS] 1332 题解 【Kosaraju】【Tarjan】

    Code[VS] 1332 上白泽慧音题解 Tarjan Algorithm Kosaraju Algorithm 题目传送门:http://codevs.cn/problem/1332/   题目描 ...

  2. Tarjan 算法&模板

    Tarjan 算法 一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连 ...

  3. Kosaraju与Tarjan(图的强连通分量)

    Kosaraju 这个算法是用来求解图的强连通分量的,这个是图论的一些知识,前段时间没有学,这几天在补坑... 强连通分量: 有向图中,尽可能多的若干顶点组成的子图中,这些顶点都是相互可到达的,则这些 ...

  4. tarjan算法,一个关于 图的联通性的神奇算法

    一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连通(strongly ...

  5. Tarjan 详解

    Tarjan 算法 一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连 ...

  6. Kosaraju 算法

    Kosaraju 算法 一.算法简介 在计算科学中,Kosaraju的算法(又称为–Sharir Kosaraju算法)是一个线性时间(linear time)算法找到的有向图的强连通分量.它利用了一 ...

  7. BZOJ 1179 Atm 题解

    BZOJ 1179 Atm 题解 SPFA Algorithm Tarjan Algorithm Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来 ...

  8. 【Apio2009】Bzoj1179 Atm

    目录 List Description Input Output Sample Input Sample Output HINT Solution Code Dfs 记忆化搜索 Position: h ...

  9. 迷宫城堡+算法讲解【tarjian算法】

    Tarjan 算法 参考博客:https://www.cnblogs.com/shadowland/p/5872257.html 算法讲解 Tarjan 算法一种由Robert Tarjan提出的求解 ...

随机推荐

  1. Jmeter在windows上安装和环境配置

    一.Jmeter简单介绍 Apache JMeter是Apache组织开发的基于Java的压力测试工具.它可以用于测试静态和动态资源,例如静态文件.Java小服务程序.CGI 脚本.Java 对象.数 ...

  2. Pycharm 专业版安装

    1.Pycharm官网,网址:http://www.jetbrains.com/pycharm/download/#section=windows,下载PyCharm安装包,根据自己电脑的操作系统进行 ...

  3. [USACO] 奶牛零食 Treats for the Cows

    题目描述 约翰经常给产奶量高的奶牛发特殊津贴,于是很快奶牛们拥有了大笔不知该怎么花的钱.为此,约翰购置了N(1≤N≤2000)份美味的零食来卖给奶牛们.每天约翰售出一份零食.当然约翰希望这些零食全部售 ...

  4. JDK的下载---官方

    1.去到官方网站 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 根据箭头选择, ...

  5. mysql insert一条记录后 返回创建记录主键id的方法

    mysql插入数据后返回自增ID的方法 mysql和oracle插入的时候有一个很大的区别是,oracle支持序列做id,mysql本身有一个列可以做自增长字段,mysql在插入一条数据后,如何能获得 ...

  6. vue-cli 3.x 配置多环境

    思路:新建一个 process.env 变量. 把 webpack 配置放到 vue.config.js 里面. 如果根目录下没有该文件,新建.配置参考:https://cli.vuejs.org/z ...

  7. python3虚拟环境应用

    python3自带虚拟环境venv,大致操作只有三步 1. 创建虚拟环境 python3 -m venv venv(名称随意) 2. 激活虚拟环境 source venv/bin/activate 3 ...

  8. Python进阶-打包程序为exe

    操作系统:win7 x64 运行环境:Python3.5 安装PyInstaller 第一步:下载PyInstaller https://github.com/pyinstaller/pyinstal ...

  9. ISO7220M芯片调试总结

    3.3V或者5V供电        速度可以达到150Mbps      有25年的寿命 调试问题总结: 在调试中发现,芯片焊接的时候很容易损坏,甚至350度焊接,时间在5s的时间都会坏掉.当坏掉的时 ...

  10. Java基础学习总结(77)——Java枚举再总结

    在Java SE5之前,我们要使用枚举类型时,通常会使用static final 定义一组int常量来标识,代码如下 public static final int MAN = 0; public s ...