前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点。

本篇文章资料参考:李煜东《算法竞赛进阶指南》

这一篇我们讲如何用Tarjan算法求有向图的SCC( 强连通分量 )已经如何缩点。

给定一张有向图,若对于图中任意两个节点x和y,

既有x到y的路径,又有y到x的路径,则该有向图是一张“强连通图”。

有向图的极大连通子图被称为“强连通分量”,即SCC。

一个环一定是强连通图。如果既有x到y的路径,又有y到x的路径,那么x和y就一定在一个环中。

这就是Tarjan算法的原理:对于每个点x,找到与它一起能构成环的所有点。

下面介绍有向图中的三种边(x,y):

1. 树枝边:搜索树中x是y的父节点

2. 前向边:搜索树中x是y的祖先节点

3. 后向边:搜索树中y是x的祖先节点

4. 横叉边:除了以上三种情况外的边,满足dfn[y]<dfn[x]

这里只给出简单定义,不再赘述。

我们可以发现,用Tarjan算法求SCC时,后向边(x,y)可以和搜索树上从y到x的路径构成一个环。

除后向边外,通过横叉边也可能找到一条从y出发能回到x的祖先节点的路径。

那么为了找到通过横叉边和后向边构成的环,Tarjan算法在dfs的过程中维护一个栈,当访问到节点x时,栈中需要保存以下两类节点:

1. 搜索树上x的祖先节点,记为集合anc(x)。设y∈anc(x),若存在一条后向边(x,y),则(x,y)和y到x之间的路径一起形成环。

2. 已经访问过,并且存在一条路径到达anc(x)的节点。

设z时一个这样的点,从z出发存在一条路径到达y∈anc(x)。若存在横叉边(x,y),则(x,z)、z到y的路径、y到x的路径形成一个环。

综上,栈中的节点就是能从x出发点的“后向边”和“横叉边”形成环的节点。

至此,我们引入追溯值low[x]的概念,有向图的Tarjan算法里面的定义和无向图是不一样的。

还是设subtree(x)表示以x为根的子树。x的追溯值low[x]定义为满足一下条件的节点的最小dfn:

1. 该点在栈中 2. 存在一条从subtree(x)出发的有向边,以该点为终点

根据以上定义,Tarjan算法根据以下步骤计算low[x]:

1. 当节点x第一次被访问时,将x入栈,初始化low[x]=dfn[x]

2. 扫描从头x出发的每条边(x,y),若y没被访问过,则说明(x,y)时树枝边,递归访问y,从y回溯之后,令low[x]=min(low[x],low[y]),若y被访问过且y在栈中,令low[y]=min(low[x],dfn[y])

3. 从x回溯之前,判断是否有low[x]=dfn[x],若成立,则不断从栈中弹出节点直至x出栈。

SCC的判定法则:

在上面的计算步骤3中,从栈中从x到栈顶的所有节点构成一个SCC。

少废话,上代码!

好der~

#include<bits/stdc++.h>
#define N 1000010
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],tot=;
inline void addedge(int f,int t){
nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
}
int dfn[N],low[N],stk[N],ins[N],c[N];
vector<int> scc[N];
int n,m,cnt,top,num;
void tarjan(int x){
dfn[x]=low[x]=++cnt;
stk[++top]=x,ins[x]=;
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]);//搜索树上的点
}else if(ins[y])
low[x]=min(low[x],dfn[y]);//y在栈中且y被访问过了
   }
  if(dfn[x]==low[x]){
num++;int z;//新的一个SCC
do{
z=stk[top--],ins[z]=;//弹出栈顶元素z
c[z]=num,scc[num].push_back(z);//z插入存第num个SCC的vector里
}while(z!=x);//直到x被弹出栈
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;i++){
int x=read(),y=read();
addedge(x,y);
}
for(int i=;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=;i<=num;i++){
printf("%d:",i);
for(int j=;j<scc[i].size();j++){
printf(" %d",scc[i][j]);
}
putchar();
}
return ;
}

SCC的缩点就非常简单了,上面我们已经用c[x]储存了每个点所在的SCC的编号,那我们直接类似e-DCC的缩点,把每个SCC缩成一个点,若c[x]≠c[y],我们就在编号为c[x]和c[y]的SCC中连一条边就可以得到一个有向无环图( DAG )。

代码真的非常简单,甚至不需要再跑一遍dfs。

给出代码:

for(int x=;x<=n;x++)
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(c[x]==c[y])continue;
addedge_c(c[x],c[y]);
}
//够简单了吧...

整个程序的代码我就不贴出来了,建新图和我前面e-DCC缩点的博客完全一致。

下一篇讲点数学,别忘了来听课。

[Tarjan系列] Tarjan算法与有向图的SCC的更多相关文章

  1. [Tarjan系列] Tarjan算法求无向图的桥和割点

    RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...

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

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

  3. Tarjan算法求有向图强连通分量并缩点

    // Tarjan算法求有向图强连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> #inc ...

  4. [Tarjan系列] 无向图e-DCC和v-DCC的缩点

    上一篇讲了如何应用Tarjan算法求出e-DCC和v-DCC. 那么这一篇就是e-DCC和v-DCC的应用之一:缩点. 先讲e-DCC的缩点. 我们把每一个e-DCC都看成一个节点,把所有桥边(x,y ...

  5. Kosaraju 算法检测有向图的强连通性

    给定一个有向图 G = (V, E) ,对于任意一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的,则说明该图 G 是强连通的(Strong ...

  6. HDU 1269 -- 迷宫城堡【有向图求SCC的数目 &amp;&amp; 模板】

    迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  7. 【数据结构与算法】多种语言(VB、C、C#、JavaScript)系列数据结构算法经典案例教程合集目录

    目录 1. 专栏简介 2. 专栏地址 3. 专栏目录 1. 专栏简介 2. 专栏地址 「 刘一哥与GIS的故事 」之<数据结构与算法> 3. 专栏目录 [经典回放]多种语言系列数据结构算法 ...

  8. Tarjan系列算法总结(hdu 1827,4612,4587,4005)

    tarjan一直是我看了头大的问题,省选之前还是得好好系统的学习一下.我按照不同的算法在hdu上选题练习了一下,至少还是有了初步的认识.tarjan嘛,就是维护一个dfsnum[]和一个low[],在 ...

  9. tarjan算法-解决有向图中求强连通分量的利器

    小引 看到这个名词-tarjan,大家首先想到的肯定是又是一个以外国人名字命名的算法.说实话真的是很佩服那些算法大牛们,佩服得简直是五体投地啊.今天就遇到一道与求解有向图中强连通分量的问题,我的思路就 ...

随机推荐

  1. sql server 建表,主键与外键约束

    主键: 能唯一区分表中每一行 外键:为某表的一列,是另一个表的主键,外键定义了两表之间的联系 商品类别表 use eshopgocreate table category( name varchar( ...

  2. 品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”

    上一篇文章强调了bean定义注册占Spring应用的半壁江山.而且详细介绍了两个重量级的注册bean定义的类. 今天就以SpringBoot为例,来看看整个SpringBoot应用的bean定义是如何 ...

  3. Storm入门,看这篇就够了

    部分一:Srorm 简介 1.1 Storm是实时的数据流,Hadoop是批量离线数据 起源背景 Twitter 开源的一个类似于Hadoop的实时数据处理框架 Storm是由Nathan Marz ...

  4. SpringBoot起飞系列-Web开发(四)

    一.前言 从今天你开始我们就开始进行我们的web开发,之前的一篇用SpringBoot起飞系列-使用idea搭建环境(二)已经说明了我们如何进行开发,当然这是搭建起步,接下来我们就开始进行详细的开发, ...

  5. go 学习笔记之10 分钟简要理解 go 语言闭包技术

    闭包是主流编程语言中的一种通用技术,常常和函数式编程进行强强联合,本文主要是介绍 Go 语言中什么是闭包以及怎么理解闭包. 如果读者对于 Go 语言的闭包还不是特别清楚的话,可以参考上一篇文章 go ...

  6. mybatis-dynamic-query 3.0 更新

    项目地址: mybatis-dynamic-query 前言 在 2.0 完成对 tk.mapper 集成,为何 mybatis-dynamic-query 选择 tk.mapper 集成, 再 3. ...

  7. 引用极光jar包之后出现控制台日志打印不出来的问题。解决!

    由于极光的jar包中引用的有log4j,项目本身也引用有log4j,如果版本有冲突,则会出现控制台日志记录打印不出来的现象.解决:引用极光jar包的时候,排除log4j. <dependency ...

  8. 制作你的第一个dockerfile文件

    From this lesson you will从这里你将学到1,Make a Dockerfile制作一个Dockerfile2,Build a Docker Image and run构建镜像并 ...

  9. golang学习之路

    目录 go语言介绍 开发环境准备 go语言基础 Go语言常用标准库 数据库相关 前端相关 web开发 go语言介绍 为什么要学习go语言 开发环境准备 从零开始搭建Go语言开发环境 VS Code配置 ...

  10. Windows和Mac系统下安装Docker

    在windows和mac系统中使用Docker Desktop安装Docker对系统的要求是很高的. 对于 Windows 系统来说,安装 Docker for Windows 需要符合以下条件: 必 ...