模板题:B3609 [图论与代数结构 701] 强连通分量

题目描述

给定一张 n 个点 m 条边的有向图,求出其所有的强连通分量。

注意,本题可能存在重边和自环。

输入格式

第一行两个正整数 n , m ,表示图的点数和边数。

接下来 m 行,每行两个正整数 u 和 v 表示一条边。

输出格式

第一行一个整数表示这张图的强连通分量数目。

接下来每行输出一个强连通分量。第一行输出 1 号点所在强连通分量,第二行输出 2 号点所在强连通分量,若已被输出,则改为输出 3 号点所在强连通分量,以此类推。每个强连通分量按节点编号大小输出。

本题让我们求出强连通分量的数量以及各强连通分量所包含的点。
解决这个问题需要用到taryan算法,下面简要介绍一下该算法的实现。

定义如下概念:
dfn[x]
可以这么理解,对一张图上所有没有遍历过的点进行dfs遍历,dfn[x]就是x点被遍历到的次序。又称dfs序。
low[x]
x所能到达的点中最小的dfs序

在一个强连通分量中,每两个点都是可以互相到达的,那么如果对于点x,low[x]!=dfn[x],说明x可以访问到比它早遍历的点。

若dfn[x]=low[x],说明点x能到达的dfs序最小的点就是x,找到了一个新的强连通分量。

使用栈存储遍历途中经过的点。

代码

#include<bits/stdc++.h>
using namespace std;
const int h=10001;
int head[h],last[h*10],to[h*10],tot=0;
void add_edge(int x,int y){
tot++;
last[tot]=head[x];
head[x]=tot;
to[tot]=y;
} int cnt=0;//标记强连通分量的数量
int timedrop=0;//标记每个点被访问的“时间”
int dfn[h];//dfn存的是这一点被访问的时间
int low[h];//low存的是 这个点可以到达的 “访问时间”最早的点
int belong[h];//存储某个点所属的强连通分量的编号
vector<int>scc[h];
stack<int>s;
bool instack[h];//标记这个点在不在栈内
bool printed[h];//存储该强连通分量是否被输出过
void dfs(int x){
//这是整个程序的核心部分,即如何求强连通分量 timedrop++;
dfn[x]=timedrop;//标记这个点被访问到的时间
low[x]=timedrop;//当前这个点能到达的“访问时间”最早的点只有它自己,以后可能会更新
s.push(x);//将这一点压入栈中
instack[x]=1;
for(int i=head[x];i!=0;i=last[i]){
//这里开始遍历该点可以到达的点,更新这一点的low
int y=to[i];
if(!dfn[y]){//这一点还没有被访问
dfs(y);//那么我们先得到这一点的dfn与low
low[x]=min(low[x],low[y]);//然后用这一点更新当前点
}
else
if(instack[y])//如果这一点在栈中,那这肯定是一个在x之前被访问的点
low[x]=min(low[y],low[x]);
//如果dfn[y]>0且它不在栈中呢?
//那么y已经找到了自己的强连通分量,和x没有关系了
}
if(dfn[x]==low[x]){
//这说明x不能到达在它之前被访问的点
//那么x就是它所在的强连通分量中第一个被访问的点
//并且在这个强连通分量中的所有点已经被压入栈中
//把这些点“取出”即可
cnt++;//新强连通分量内所有的点已经找出
while(s.top()!=x){
int q=s.top();
belong[q]=cnt;
instack[q]=0;
scc[cnt].push_back(q);
s.pop();
}
belong[x]=cnt;
instack[x]=0;
scc[cnt].push_back(x);
s.pop();
} }
int n,m;
int main(){
scanf("%d%d",&n,&m);
//建图
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
add_edge(a,b);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
dfs(i);
printf("%d\n",cnt);
//使用sort对每个强连通分量内的点进行升序排列(以前我也不知道)
for(int i=1;i<=cnt;++i)
sort(scc[i].begin(),scc[i].end());
for(int i=1;i<=n;i++){
int y=belong[i];
if(!printed[y]){
for(int j=0;j<scc[y].size();j++)
printf("%d ",scc[y][j]);
printf("\n");
printed[y]=1;
}
else
continue;
}
return 0;
}

强连通分量与tarjan算法初步运用的更多相关文章

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

    有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G ...

  2. 强连通分量的Tarjan算法

    资料参考 Tarjan算法寻找有向图的强连通分量 基于强联通的tarjan算法详解 有向图强连通分量的Tarjan算法 处理SCC(强连通分量问题)的Tarjan算法 强连通分量的三种算法分析 Tar ...

  3. 【转】有向图强连通分量的Tarjan算法

    原文地址:https://www.byvoid.com/blog/scc-tarjan/ [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly con ...

  4. 算法笔记_144:有向图强连通分量的Tarjan算法(Java)

    目录 1 问题描述 2 解决方案 1 问题描述 引用自百度百科: 如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连 ...

  5. 【转载】有向图强连通分量的Tarjan算法

    转载地址:https://www.byvoid.com/blog/scc-tarjan [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly conn ...

  6. 有向图强连通分量的Tarjan算法(转)

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

  7. 『图论』有向图强连通分量的Tarjan算法

    在图论中,一个有向图被成为是强连通的(strongly connected)当且仅当每一对不相同结点u和v间既存在从u到v的路径也存在从v到u的路径.有向图的极大强连通子图(这里指点数极大)被称为强连 ...

  8. 有向图强连通分量的Tarjan算法及模板

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

  9. 【强连通分量】tarjan算法及kosaraju算法+例题

    阅读前请确保自己知道强连通分量是什么,本文不做赘述. Tarjan算法 一.算法简介 Tarjan算法是一种由Robert Tarjan提出的求有向图强连通分量的时间复杂度为O(n)的算法. 首先我们 ...

随机推荐

  1. class 中的 构造方法、static代码块、私有/公有/静态/实例属性、继承 ( extends、constructor、super()、static、super.prop、#prop、get、set )

     part 1         /**          * << class 中的 static 代码块与 super.prop 的使用          *          * - ...

  2. KingbaseES通过sys_waldump解析wal日志

    前言 oracle中的redo日志我们无法直接读取,然而对于KingbaseES数据库,我们可以利用sys_waldump工具解析wal日志,查看wal日志记录的信息. 我们可以利用 sys_wald ...

  3. 字符类数据类型和oracle字符类型的区别

    为兼容Oracle的数据类型,KingbaseES扩展了Oracle的NUMBER.VARCHAR2.CHAR(n)和DATE类型.该措施使得移植Oracle的Create Table等DDL语句时, ...

  4. 如何使用Arthas定位问题

    在我们日常的工作中,经常会遇到一些线上才会遇到的问题.Arthas无疑是我们在工作中,定位线上问题的神奇.下面,我将介绍一下我们在工作中经常用到的一些功能. dashboard 首先我们可以通过das ...

  5. Elasticsearch:使用_update_by_query更新文档

    转载自: https://blog.csdn.net/UbuntuTouch/article/details/105564270 在很多的情况下,我们我们想更新我们所有的文档: 添加一个新的field ...

  6. jumpserver 2222端口的使用

    可以这样理解: 通过在jumpserver的web界面添加的用户,相应的也有权限通过远程使用命令的方式登陆jumpserver,进行相应的管理,只不过使用的端口是2222端口,不是常见的22端口. 一 ...

  7. C语言下for循环的一点技巧总结

    for循环是普遍应用与各种计算机语言的一种循环方式. 一般情况下, for循环规则:for(条件一:条件二:条件三) 条件一为满足条件,也就是条件一为1时,进入这个for循环.条件二为循环条件,也就是 ...

  8. UML类中的6种关系

    最近现看Java设计模式,但是越看越不明白,一直搞不明白类与类之前的关系有哪些,通过几天的学习与整理通过代码实现的UML画图的方式理解类之间的相互关系. 一.类与类6大关系: 泛化(generaliz ...

  9. pthread_mutex_t & pthread_cond_t 总结

    pthread_mutex_t & pthread_cond_t 总结 一.多线程并发 1.1 多线程并发引起的问题 我们先来看如下代码: #include <stdio.h> # ...

  10. LOJ2324「清华集训 2017」小Y和二叉树

    题目链接 瞎jb贪一发就过了.首先度数<=2且编号最小的点一定是中序遍历最靠前的点,我们从这个点开始dfs一遍算出子树中度数<=2且编号最小的点记为\(f(i)\),然后从这个点开始一步一 ...