<更新提示>

<第一次更新>


<正文>

有向图的强连通分量

定义:在有向图\(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. spring boot 项目打成war包部署到服务器

    这是spring boot学习的第二篇了,在上一篇已经整合了spring boot项目了,如果还有小伙伴没有看得可以先去看第一篇 基础整合spring boot项目 到这里的小伙伴应该都是会整合基本的 ...

  2. 表关联ID相同数据update修改

    UPDATE 表1 e,表2 c SET e.被修改字段='修改值为..' WHERE e.id=c.id

  3. Ext使用中问题总结

    隐藏 Ext.form.DateField 的触发(trigger)元素使其内容不能修改并使其所有的文本框(text field)显示格式为Y-m-d items : [{ xtype : ' dat ...

  4. MSDN i TELL YOU 又更新了,win10 1809版本的 3月29日的

    MSDN i TELL YOU 又更新了,1809版本的 3月29日的 WINDOWS 10 现在只有64位的 很好,估计 64位的普及了. 是一大改变

  5. PBRT笔记(7)——反射模型

    基础术语 表面反射可以分为4大类: diffuse 漫反射 glossy specular 镜面反射高光 perfect specular 完美反射高光 retro-reflective distri ...

  6. hyperopt自动调参

    hyperopt自动调参 在传统机器学习和深度学习领域经常需要调参,调参有些是通过通过对数据和算法的理解进行的,这当然是上上策,但还有相当一部分属于"黑盒" hyperopt可以帮 ...

  7. webpack2入门概念

    webpack是一种JavaScript应用模块化打包工具,它配置起来简单易上手,因此很多企业工程化代码都使用它来打包.在具体介绍如何使用webpack之前,先来介绍下webpack的四个核心概念. ...

  8. python 面试题知识回顾

    1. python 函数 的参数传递 a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun(a) ...

  9. CrypMic分析报告

    一.概述 病毒伪装为NirSoft公司的软件NirCmd并加了MPRESS壳,脱壳后是一个混淆过的PE程序,运行时会用到类似PE映像切换的方式来释放出实际的恶意代码,恶意代码主要对文件进行加密. 二. ...

  10. ThreadLocal是否会导致内存泄露

    什么是内存泄露? 维基百科的定义:[内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存],我的理解就是程序失去了对某段内存的控制,那么这段内存就算是泄露了. ThreadLocal为什么会导致 ...