[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,大家首先想到的肯定是又是一个以外国人名字命名的算法.说实话真的是很佩服那些算法大牛们,佩服得简直是五体投地啊.今天就遇到一道与求解有向图中强连通分量的问题,我的思路就 ...
随机推荐
- 搭建maven本地仓库,idea应用本地maven仓库
提环境: 安装好tomcat.maven.jdk.idea. 打开maven安装目录,在目录下新建 maven仓库文件夹(名子随便): 打开conf\setting.xml 加入<localRe ...
- RestClient火狐接口测试
一.RestClient的简单介绍 RESTClient是一款用于测试各种Web服务的插件,它可以向服务器发送各种HTTP请求(用户也可以自定义请求方式),并显示服务器响应.二.RESTClient的 ...
- linux下安装pip(centos)
centos系统中自带python2.7.5但是却没有pip工具 直接yum install pip会提示没有这个包 解决方案: 需要先安装扩展源EPEL. EPEL(http://fedorapro ...
- zookeeper特性与节点说明
一.zookeeper概要.背景及作用 zookeeper产生背景: 项目从单体到分布式转变之后,将会产生多个节点之间协同的问题.如: 每天的定时任务由谁哪个节点来执行? RPC调用时的服务发现? 如 ...
- CAS详细登录流程(转)
转:https://www.cnblogs.com/lihuidu/p/6495247.html 4.CAS的详细登录流程 上图是3个登录场景,分别为:第一次访问www.qiandu.com.第二次访 ...
- 动态set mybatis与ibatis的写法
mybatis: <set> <if test="obj.buyerId != null"> buyerId = #{obj.buyerId}, </ ...
- 异步Promise及Async/Await最完整入门攻略
一.为什么有Async/Await? 我们都知道已经有了Promise的解决方案了,为什么还要ES7提出新的Async/Await标准呢? 答案其实也显而易见:Promise虽然跳出了异步嵌套的怪圈, ...
- k阶斐波那契数列fibonacci第n项求值
已知K阶斐波那契数列定义为:f0 = 0, f1 = 0, … , fk-2 = 0, fk-1 = 1;fn = fn-1 + fn-2 + … + fn-k , n = k , k + 1, … ...
- Django基础之jQuery操作cookie
jquery之cookie操作 定义:让网站服务器把少量数据储存到客户端的硬盘或内存,从客户端的硬盘读取数据的一种技术: 下载与引入:jquery.cookie.js基于jquery:先引入jquer ...
- XCTF-CAT
果然还是我太菜了呜呜呜,这道题仍然是没有自己做出来.哎. 这一道用的并不是PHP的环境,而是用Python中的Django编写的. 记得做过类似的一道题目.来源于MOCTF中的网站扫描器,当时做完后其 ...