『Tarjan算法 有向图的强连通分量』
<更新提示>
<第一次更新>
<正文>
有向图的强连通分量
定义:在有向图\(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算法 有向图的强连通分量』的更多相关文章
- Tarjan算法求出强连通分量(包含若干个节点)
[功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量.强连通分量是指有向图G里顶点间能互相到达的子图.而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连 ...
- Kosaraju算法 有向图的强连通分量
有向图的强连通分量即,在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...
- poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)
/* 题目大意:有N个cows, M个关系 a->b 表示 a认为b popular:如果还有b->c, 那么就会有a->c 问最终有多少个cows被其他所有cows认为是popul ...
- Tarjan算法初探 (1):Tarjan如何求有向图的强连通分量
在此大概讲一下初学Tarjan算法的领悟( QwQ) Tarjan算法 是图论的非常经典的算法 可以用来寻找有向图中的强连通分量 与此同时也可以通过寻找图中的强连通分量来进行缩点 首先给出强连通分量的 ...
- 『Tarjan算法 无向图的双联通分量』
无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...
- 【有向图】强连通分量-Tarjan算法
好久没写博客了(都怪作业太多,绝对不是我玩的太嗨了) 所以今天要写的是一个高大上的东西:强连通 首先,是一些强连通相关的定义 //来自度娘 1.强连通图(Strongly Connected Grap ...
- Tarjan算法 求 有向图的强连通分量
百度百科 https://baike.baidu.com/item/tarjan%E7%AE%97%E6%B3%95/10687825?fr=aladdin 参考博文 http://blog.csdn ...
- [有向图的强连通分量][Tarjan算法]
https://www.byvoid.com/blog/scc-tarjan 主要思想 Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的 ...
- Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法
一.背景介绍 强连通分量是有向图中的一个子图,在该子图中,所有的节点都可以沿着某条路径访问其他节点.强连通性是一种非常重要的等价抽象,因为它满足 自反性:顶点V和它本身是强连通的 对称性:如果顶点V和 ...
随机推荐
- html动画实现
1.目标动图 2.主要代码 <p><img src="http://img.baidu.com/hi/jx2/j_0043.gif"/><img sr ...
- python_str的应用
name = "fsafalk" #nam是个变量名 fsafalk是变量 也是字符串 name.startswith('fs')#判断是否是fs开头 name.endswit ...
- Unity Bolt插件 基本使用
1.Bolt的安装和配置 导入插件后可以看到 设置命名方式,左侧:普通人,右侧: 程序员 设置变量类型(可以手动添加自己自定义的类型) 然后点击生成,等待bolt编译生成. 2.创建一个流程并使用 如 ...
- Numpy 基础运算1
# -*- encoding:utf-8 -*- # Copyright (c) 2015 Shiye Inc. # All rights reserved. # # Author: ldq < ...
- JUnit介绍(转)
测试的重要性毋庸再说,但如何使测试更加准确和全面,并且独立于项目之外并且避免硬编码,JUnit给了我们一个很好的解决方案.一.引子 首先假设有一个项目类SimpleObject如下: pu ...
- docker 安装 zookeeper
镜像下载hub.docker.com 上有不少 ZK 镜像, 不过为了稳定起见, 我们就使用官方的 ZK 镜像吧.首先执行如下命令: docker pull zookeeper当出现如下结果时, 表示 ...
- ip锁死怎么设置ip地址
单击电脑网络连接图标,打开网络和共享中心 点击本地连接 点击详细信息,即可看到IP地址.子网掩码.默认网关.DNS服务器信息 再点击本地连接状态下的属性 找到Internet 协议版本 ...
- Django ORM 知识概要
相关命令 python3 manage.py makemigrations 根据模型生成相关迁移文件 python3 manage.py migrate 根据迁移文件,将表结构更新到数据库中,并在Dj ...
- oracle中文乱码问题解决
中文乱码问题解决:1.查看服务器端编码select userenv('language') from dual;我实际查到的结果为:AMERICAN_AMERICA.ZHS16GBK2.执行语句 se ...
- Java作业八(2017-10-30)
public class TAutoPerson { public static void main(String args[]) { new Person(); new Person(); new ...