<更新提示>

<第一次更新>


<正文>

有向图的强连通分量

定义:在有向图\(G\)中,如果两个顶点\(v_i,v_j\)间\((v_i>v_j)\)有一条从\(v_i\)到\(v_j\)的有向路径,同时还有一条从\(v_j\)到\(v_i\)的有向路径,则称两个顶点强连通(\(strongly\ connected\))。如果有向图\(G\)的每两个顶点都强连通,称\(G\)是一个强连通图。有向图的极大强连通子图,称为强连通分量(\(strongly\ connected\ components\))。

万能的\(Tarjan\)算法也可以帮助我们求解有向图的强联通分量。

预备知识

时间戳

图在深度优先遍历的过程中,按照每一个节点第一次被访问到的顺序给\(N\)个节点\(1-N\)的标记,称为时间戳,记为\(dfn_x\)。

追溯值

设节点\(x\)可以通过搜索树以外的边回到祖先,那么它能回到祖先的最小时间戳称为节点\(x\)的追溯值,记为\(low_x\)。当\(x\)没有除搜索树以外的边时,\(low_x=x\)。

Tarjan 算法

著名的\(Tarjan\)算法可以在线性时间内求解有向图的强联通分量。

  • 举个栗子,右图中,子图\(\{1,2,3,4\}\)为一个强连通分量,因为顶点\(1,2,3,4\)两两可达。\(\{5\},\{6\}\)也分别是两个强连通分量。

\(Tarjan\)求强连通分量的过程仍然是在递归求解\(dfn\),\(low\)的过程中利用这两个数组实现的(如何求解可以参见(『Tarjan算法 无向图的割点与割边』),其原理如下。

\(Tarjan\)算法将每个强连通分量看作图的搜索树中的一棵子树,搜索时,将每一个未回溯的节点加入一个栈,回溯时若\(dfn\)值与\(low\)值相等,则得到栈顶的当前节点以上的若干节点即为一个强连通分量。

我的理解:

回溯时若\(dfn\)值与\(low\)值相等,则说明以当前节点为根的子树中的若干节点都通过直接或间接路径返回到了当前节点,而当前节点到那些节点显然是可行的。也就是说,它们形成了若干个环,构成了一个强连通分量。

实际上,\(low\)数组就是不断在找"环"结构的过程。

其流程如下:

对于每一个当前访问的点:

1.更新\(dfn\)和\(low\)的初始标记,\(low=dfn\)

2.遍历当前节点的每一个子节点

3.如果其子节点未标记\(dfn\)值,访问并更新,并顺带更新\(low\)值

4.如果已经访问标记了\(dfn\)值,并且其子节点还在栈中,则该边是一条返祖边,更新\(low\)值

5.完成所有子节点的遍历后,判断\(dfn\)是否等于\(low\),若相等,则说明当前栈顶的若干点(直到栈顶节点为当前节点)构成了一个强连通分量,记录即可

\(Code:\)

inline void Tarjan(int x)
{
dfn[x]=low[x]=++cnt;
Stack.push(x);inSta[x]=true;
for(int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!dfn[y])
{
Tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(inSta[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
int top=0;tot++;
while(top!=x)
{
top=Stack.top();
Stack.pop();
inSta[top]=false;
con[top]=tot;
size[tot]++;
//这些点都在编号为tot的一个强连通分量中,con为查询强连通分量的数组,size为强连通分量的大小
//储存方式需要适时改变,以应合题目
}
}
}

Tarjan算法的应用

通常,我们可以通过\(tarjan\)算法找到有向图中的强连通分量,若将各个强连通分量压缩成一个点,我们就得到了一个有向无环图(\(DAG\)),这对我们的解题过程可以有所帮助。

最受欢迎的牛

Description

每头牛都有一个梦想:成为一个群体中最受欢迎的名牛!

在一个有N(1<=N<=10,000)头牛的牛群中,给你M(1<=M<=50,000)个二元组(A,B),表示A认为B是受欢迎的。

既然受欢迎是可传递的,那么如果A认为B受欢迎,B又认为C受欢迎,则A也会认为C是受欢迎的,哪怕这不是十分明确的规定。你的任务是计算被所有其它的牛都喜欢的牛的个数。

Input Format

第一行,两个数,N和M。

第2~M+1行,每行两个数,A和B,表示A认为B是受欢迎的。

Output Format

一个数,被其他所有奶牛认为受欢迎的奶牛头数。

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

解析

将牛的欢迎关系视为图的连边后,我们就得到了一张有向图,不过不能保证无环。

我们放宽限制,假设给出的是有向无环图,可以尝试几组样例。

发现规律后我们可以得到猜想:若有且仅有一个点出度为0,则该点符合要求,答案总数为1,若有多于一个点出度为0,则没有符合要求的点,答案总数为0。

那么对于原图,我们把每一个强连通分量压缩为一个点,按有向无环图的规律得到答案即可。若符合要求的点是一个由强连通分量压缩得到的点,则答案数量为该强连通分量的大小。

这就成了一道强连通分量缩点模板题。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int N=20000+200,M=80000+200;
int n,m,dfn[N],low[N],cnt,Last[M*2],t,con[N],tot,inSta[N],outdeg[N],size[N],ans=0;
stack < int > Stack;
struct edge{int ver,next;}e[M*2];
inline void insert(int x,int y)
{
e[++t].ver=y;e[t].next=Last[x];Last[x]=t;
}
inline void input(void)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
insert(x,y);
}
}
inline void Tarjan(int x)
{
dfn[x]=low[x]=++cnt;
Stack.push(x);inSta[x]=true;
for(int i=Last[x];i;i=e[i].next)
{
int y=e[i].ver;
if(!dfn[y])
{
Tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(inSta[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
int top=0;tot++;
while(top!=x)
{
top=Stack.top();
Stack.pop();
inSta[top]=false;
con[top]=tot;
size[tot]++;
}
}
}
inline void build(void)
{
for(int i=1;i<=n;i++)
for(int j=Last[i];j;j=e[j].next)
if(con[i]^con[e[j].ver])outdeg[con[i]]++;
}
inline void find(void)
{
int flag=0;
for(int i=1;i<=tot;i++)
if(!outdeg[i])
{
if(!flag)flag=i;
else
{
ans=0;
return;
}
}
ans=size[flag];
}
int main(void)
{
input();
for(int i=1;i<=n;i++)
if(!dfn[i])Tarjan(i);
build();
find();
printf("%d\n",ans);
return 0;
}

<后记>

『Tarjan算法 有向图的强连通分量』的更多相关文章

  1. Tarjan算法求出强连通分量(包含若干个节点)

    [功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量.强连通分量是指有向图G里顶点间能互相到达的子图.而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连 ...

  2. Kosaraju算法 有向图的强连通分量

    有向图的强连通分量即,在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...

  3. poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)

    /* 题目大意:有N个cows, M个关系 a->b 表示 a认为b popular:如果还有b->c, 那么就会有a->c 问最终有多少个cows被其他所有cows认为是popul ...

  4. Tarjan算法初探 (1):Tarjan如何求有向图的强连通分量

    在此大概讲一下初学Tarjan算法的领悟( QwQ) Tarjan算法 是图论的非常经典的算法 可以用来寻找有向图中的强连通分量 与此同时也可以通过寻找图中的强连通分量来进行缩点 首先给出强连通分量的 ...

  5. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  6. 【有向图】强连通分量-Tarjan算法

    好久没写博客了(都怪作业太多,绝对不是我玩的太嗨了) 所以今天要写的是一个高大上的东西:强连通 首先,是一些强连通相关的定义 //来自度娘 1.强连通图(Strongly Connected Grap ...

  7. Tarjan算法 求 有向图的强连通分量

    百度百科 https://baike.baidu.com/item/tarjan%E7%AE%97%E6%B3%95/10687825?fr=aladdin 参考博文 http://blog.csdn ...

  8. [有向图的强连通分量][Tarjan算法]

    https://www.byvoid.com/blog/scc-tarjan 主要思想 Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的 ...

  9. Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法

    一.背景介绍 强连通分量是有向图中的一个子图,在该子图中,所有的节点都可以沿着某条路径访问其他节点.强连通性是一种非常重要的等价抽象,因为它满足 自反性:顶点V和它本身是强连通的 对称性:如果顶点V和 ...

随机推荐

  1. Linux学习之shell

    通配符 *:表示从它所在位置开始到某个符合条件的结束符之间的任何字符 ?:表示它所在位置上的任何可能的单个字符 []:表示[]中所包含字符的任何一个 特殊键 ctrl+c  #停止当前程序执行 ctr ...

  2. I - Infinite Improbability Drive

    I - Infinite Improbability Drivehttp://codeforces.com/gym/241750/problem/I不断构造,先填n-1个0,然后能放1就放1,最后这个 ...

  3. 三、OpenStack创建域,项目,用户和角色,验证,创建客户端脚本

    一.Identity服务为每个OpenStack服务提供身份验证服务. 身份验证服务使用域,项目,用户和 角色的组合. 1.创建service 项目 # openstack project creat ...

  4. 福州大学软件工程1916|W班 第3次作业成绩排名

    作业链接 结对第二次-文献摘要热词统计及进阶需求 评分细则 本次作业由三部分组成(程序满分80,博客满分70,工程能力满分30) 程序评分标准 基础需求 共有7个测试用例,每个满分20分并按照一定的映 ...

  5. C++第三课:类的使用(一)[个人见解]

    说到C++语言的类,也称对象.在C++中首先得了解的三大特性:继承.封装.多态. 使用C++类,间接的反映出你所学习C++的深度,这章很重要,但小编未必能全部讲到,还望谅解. 类是C++语言中新添加的 ...

  6. 分布式、集群、微服务、SOA 之间的区别

    分布式:不同模块部署在不同服务器上 作用:分布式解决网站高并发带来问题 集群:多台服务器部署相同应用构成一个集群 作用:通过负载均衡设备共同对外提供服务 SOA:Service Oriented Ar ...

  7. ES6学习

    一.ES6的特点 1.let(变量),const(常量) 2.在ES6中不能重复定义 3.块级作用域 普通作用域 if(true){ var test =1; } console.log(test); ...

  8. 无法运行 vue-manage-system@3.1.0 dev: `webpack-dev-server --inline --progress --

    一个项目的变大好多人开发,难免会有很多的冲突.每次跟新代码都要一个坑一个坑的解决的.这次遇到这个坑好大.急死了.... 百度了好多说占用端口,试了好几遍不行.最终还是要去查原因的....经过了几个小时 ...

  9. over(partition by..) 的运用(转)

    oracle的分析函数over 及开窗函数一:分析函数overOracle从8.1.6开始提供分析函数,分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是对于每个组返回多行,而聚合函数对于每 ...

  10. React的类型检测PropTypes

    React.propTypes:React.PropTypes 提供很多验证器来验证传入数据的有效性,当向props传入无效数据时,JavaScript 控制台会抛出警告. ; class MyTit ...