[Tarjan系列] Tarjan算法与有向图的SCC
前面的文章介绍了如何用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的更多相关文章
- [Tarjan系列] Tarjan算法求无向图的桥和割点
RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...
- [Tarjan系列] Tarjan算法求无向图的双连通分量
这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量. 双联通分量包括点双连通分量v-DCC和边连通分量e-DCC. 若一张无向连通图不存在割点,则称它为 ...
- Tarjan算法求有向图强连通分量并缩点
// Tarjan算法求有向图强连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> #inc ...
- [Tarjan系列] 无向图e-DCC和v-DCC的缩点
上一篇讲了如何应用Tarjan算法求出e-DCC和v-DCC. 那么这一篇就是e-DCC和v-DCC的应用之一:缩点. 先讲e-DCC的缩点. 我们把每一个e-DCC都看成一个节点,把所有桥边(x,y ...
- Kosaraju 算法检测有向图的强连通性
给定一个有向图 G = (V, E) ,对于任意一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的,则说明该图 G 是强连通的(Strong ...
- HDU 1269 -- 迷宫城堡【有向图求SCC的数目 && 模板】
迷宫城堡 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- 【数据结构与算法】多种语言(VB、C、C#、JavaScript)系列数据结构算法经典案例教程合集目录
目录 1. 专栏简介 2. 专栏地址 3. 专栏目录 1. 专栏简介 2. 专栏地址 「 刘一哥与GIS的故事 」之<数据结构与算法> 3. 专栏目录 [经典回放]多种语言系列数据结构算法 ...
- Tarjan系列算法总结(hdu 1827,4612,4587,4005)
tarjan一直是我看了头大的问题,省选之前还是得好好系统的学习一下.我按照不同的算法在hdu上选题练习了一下,至少还是有了初步的认识.tarjan嘛,就是维护一个dfsnum[]和一个low[],在 ...
- tarjan算法-解决有向图中求强连通分量的利器
小引 看到这个名词-tarjan,大家首先想到的肯定是又是一个以外国人名字命名的算法.说实话真的是很佩服那些算法大牛们,佩服得简直是五体投地啊.今天就遇到一道与求解有向图中强连通分量的问题,我的思路就 ...
随机推荐
- mysql按关键词截取字符串
按关键字截取字符串 :substring_index(被截取字段,关键字,关键字出现的次数) eg:字符串:test.docx:test2.zip substring_index(file_name, ...
- Ubuntu+docker+gitlab安装和使用
以前自己写的代码都是在本地,因为都是自己一个人维护,现在交给团队维护了,所以想着搭建一个gitlab 1,拉镜像 安装非常简单 docker search gitlab 搜索镜像 docker pu ...
- jmeter入门(环境搭建&运行&初识)
最近了解了一些性能测试的基础知识和原理决定动手实践下,比较选择了jmeter 一.什么是jmeter Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试 ...
- jenkins自动化部署项目3 --设置用户
我直接设置的admin ,jenkins可以新建多个用户,并赋予不同的权限(TODO) 等后续需要严格规范操作人的时候再补充
- Selenium+python操作id为动态变化的frame(iframe)
先定位到一组frame:ele = dr.find_elements_by_tag_name('iframe')此时获得一组frame 再通过index取需要切进去的frame并取到该frame的id ...
- 2019-2020-1 20199303 《Linux内核原理分析》 第一周作业
2019-2020-1 20199303 <Linux内核原理分析> 第一周作业 1. 环境准备 在众多的Linux发行版中,Ubuntu,小红帽还有类Unix系统的BSD系统,我选择了目 ...
- 阿里云搭建nginx + uWSGI 实现 django 项目
系统版本 CentOS/7 64位 1.安装使用python3 创建python3目录 sudo mkdir /usr/local/python3 进入python3目录 cd /usr/local/ ...
- android实现emoji输入
学android也有一段时间, 一直都是自己摸索, 各种上网查资料, 也明白了不能一味去索取有时间也要分享一些自己的心得 . 最近几天都在写关于android emoji输入的小例子,网上有不少源码还 ...
- docker容器添加对外映射端口
一般在运行容器时,我们都会通过参数 -p(使用大写的-P参数则会随机选择宿主机的一个端口进行映射)来指定宿主机和容器端口的映射,例如 docker run -it -d --name [contain ...
- Python 正则re匹配中文、英式数字
#coding:utf-8 import re s = u''' 或多或少的好好读书电锯惊魂20202 和水电费后是否会时候1212没收到风10.12海大富的是粉红色的和办法的1244525.000 ...