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. Nginx 通过 certbot 为网站自动配置 SSL 证书并续期

    一.背景知识 1.1.http 和 https 是什么? 简单来说,http 是一个传输网页内容的协议,比如你看到的 http 开头的网站 http://www.163.com ,其网页上的文字.图片 ...

  2. top命令的用法

    top命令的用法 2018年07月15日 09:50:04 zhuoya_ 阅读数:1858    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/z ...

  3. css--小白入门篇1

    一.引入 css用来描述html,学习css前我们先来学习html的基础标签的用法,再进入css的学习. 本教程面向小白对象,不会讲细枝末节深入的东西. 二.列表 列表有3种 2.1 无序列表 无序列 ...

  4. CentOS7.x的DNS服务的基础配置

    一.bind服务器安装 bind:开源.稳定.应用广泛的DNS服务.bind的软件包名bind,服务名称named. 查看是否安装bind, 安装bind包: rpm -qa bind yum -y ...

  5. Python关于导入模块的一些感想:

    写项目的时候,碰到这种情况 程序业务为core,里面有两个目录,core1 和core2  core1中有三个模块,business  main   main1 程序入口为bin目录下的project ...

  6. java导出word的6种方式(转发)

    来自: http://www.cnblogs.com/lcngu/p/5247179.html 最近做的项目,需要将一些信息导出到word中.在网上找了好多解决方案,现在将这几天的总结分享一下. 目前 ...

  7. 一篇入门MongoDB

    目录 1.MongoDB 基本介绍 2.MongoDB 基本概念 3.数据库操作 4.集合操作 5.文档操作 6.查询条件 7.索引 1.MongoDB 基本介绍 (1)安装 MongoDB 简单来说 ...

  8. 关于ajax跨域请求API数据的一些问题

    一般来说我们使用jquery的ajax来跨域请求API数据的时候每次请求,就只能请求一组数据,而且当我们再次点击发送ajax请求的时候,新请求的数据会覆盖掉原来的数据,那么如何每次在请求的数据的时候, ...

  9. 2.Linux文件IO编程

    2.1Linux文件IO概述 2.1.0POSIX规范 POSIX:(Portable Operating System Interface)可移植操作系统接口规范. 由IEEE制定,是为了提高UNI ...

  10. redis学习-简介

    1. Redis 简介 1.1 Redis是什么 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统.Red ...