图论:tarjan 算法求解强连通分量

题目描述

有一个 n n n 个点, m m m 条边的有向图,请求出这个图点数大于 1 1 1 的强连通分量个数。

输入格式

第一行为两个整数 n n n m m m

第二行至 m + 1 m+1 m+1 行,每一行有两个整数 a a a b b b,表示有一条从 a a a b b b 的有向边。

输出格式

仅一行,表示点数大于 1 1 1 的强连通分量个数。

5 4
2 4
3 5
1 2
4 1
1

提示

数据规模与约定

对于全部的测试点,保证 2 ≤ n ≤ 1 0 4 2\le n \le 10^4 2n104 2 ≤ m ≤ 5 × 1 0 4 2\le m\le 5\times 10^4 2m5×104 1 ≤ a , b ≤ n 1 \leq a, b \leq n 1a,bn


基本概念梳理

强连通图

在一个有向图中,任意两个节点之间都能相互到达,那么这个图就是一个强连通图。

强连通分量(Strongly Connected Components, SCC)

强连通图是非常少的,但是在有向图中,如果存在某些节点子集 A A A,任意两个节点可以相互到达,且如果往子集 A A A 中再加入新的节点,那么子集中任意两个节点之间无法相互到达,那么称这些节点为强连通分量。如下图,共有三个强连通分量,红框中的节点 1 , 2 , 3 1,2,3 1,2,3 组成了一个强连通分量,绿框中的节点 4 , 5 4,5 4,5 组成了另一个强连通分量,蓝框中的节点 6 6 6 是一个强连通分量。
在这里插入图片描述

D F S DFS DFS 遍历

之前我们学习 D F S DFS DFS 遍历的时候,有两种常用的遍历方式。

方式 1 1 1:先访问当前节点,再递归相邻节点。(类似求树的深度)

方式 2 2 2:先递归相邻节点,再访问当前节点。(类似求子树大小)

t a r j a n tarjan tarjan 算法思路

对于 t a r j a n tarjan tarjan 算法,我们用的 d f s dfs dfs 遍历 是上面 方式 2 2 2

对于每个节点来说,有两个重要属性,我通常定义为 i d id id t x tx tx,其中 i d id id 表示 首次到达节点的时间戳, t x tx tx 表示从当前节点出发,可以遍历到的 i d id id 最小的节点的值,对于上面的图,我们可以从节点 1 1 1 出发进行一次【方式 2 2 2】 的 d f s dfs dfs 遍历,试试 可以得到每个节点的 i d id id 值和 t x tx tx 值分别是多少?如下:

节点 1 : i d = 1 , t x = 1 1: id = 1, tx = 1 1:id=1,tx=1

节点 2 : i d = 2 , t x = 1 2:id = 2, tx = 1 2:id=2,tx=1

节点 3 : i d = 3 , t x = 1 3:id = 3, tx = 1 3:id=3,tx=1

节点 4 : i d = 4 , t x = 4 4: id = 4, tx = 4 4:id=4,tx=4

节点 5 : i d = 5 , t x = 4 5: id = 5, tx = 4 5:id=5,tx=4

节点 6 : i d = 6 , t x = 6 6: id = 6, tx = 6 6:id=6,tx=6

通过观察可以发现,同一个强连通分量中的节点,它们的 t x tx tx 值是相等的,而且,强连通分量中的起点的 i d id id 一定等于 t x tx tx

在整个 d f s dfs dfs 搜索过程中,我们需要借助 栈 来保存遍历到的元素,到达某个节点后,先从这个节点开始搜,搜索结束后,更新当前节点的 t x tx tx 值,所有邻接点全部搜索完后,判断当前节点是否是当前这个强连通分量的起点(即 i d id id 是否等于 t x tx tx),如果是,从栈空间中取出当前强连通分量的所有节点即可。

比如,找第一个强连通分量,栈结构如下:
在这里插入图片描述

此时,对于节点 1 1 1 来说,在栈中的位置到栈顶,所有元素都是以 节点 1 1 1 为起点的强连通分量中的元素。

其他两个强连通分量类似,在此不画,可以通过代码来理解。

代码

#include "bits/stdc++.h"
using namespace std;
const int N = 2e4+7, M = 5e4+7;
struct Edge{int v, ne;
}es[M];
struct Node{int id, tx;
}f[N];
int n, t=1, m, u, v, idx = 1, h[N], ans;
stack<int> stk;
bool vis[N];  // 记录节点是否在栈中,在栈中标记为true,否则为false
// dfs函数,传入节点编号,开始从当前节点进行 先搜索后访问的 dfs
void dfs(int u) {f[u].id = t;  // 首次到达的时间戳f[u].tx = t++;  // 可以回溯到最小的时间戳,初始的时候就是 t,跟id 一样stk.push(u);   // 当前节点进栈vis[u] = true;  // 所有进栈的元素全部标记for(int e = h[u]; e; e = es[e].ne) {  // 链式前向星遍历节点 u 的所有邻接点int v = es[e].v;  // u 的 邻接点 vif(f[v].id == 0) {   // 节点v 还没有访问过dfs(v);  // 先搜索f[u].tx = min(f[u].tx, f[v].tx);  // 再更新可以回溯到的最小时间戳} else if(vis[v]) {  // 节点v访问过,如果在栈中,那就一定是当前强连通分量中的节点f[u].tx = min(f[u].tx, f[v].tx);  // 更新最小时间戳}}if(f[u].id == f[u].tx) {  // 节点u 是当前强连通分量中的起点,也可以成为当前强连通分量的根节点int cnt = 0; // 本次强连通分量的节点个数,计算的是大于1 的强连通分量的个数while(!stk.empty() && stk.top() != u) {  // 栈顶这些元素都是和 u 一个强连通分量的,而且 u 是这个强连通分量的鼻祖vis[stk.top()] = false;  // 出栈更新visstk.pop();  // 出栈cnt++;  // 强连通分量中节点数+1}vis[stk.top()] = false;  // 本次是 u 节点stk.pop();  // u 出栈cnt++;  // 个数+1if(cnt > 1) ans++;  // 统计点数大于 1 的强连通分量的个数}
}
// 链式前向星建图
void add(int u, int v) {es[idx] = {v, h[u]};h[u] = idx++;return;
}
int main() {cin >> n >> m;while(m--) {cin >> u >> v;add(u, v);   // 有向图}for(int u=1; u<=n; u++) {if(f[u].id == 0) dfs(u);  // 当前节点还没有遍历过,开始从当前节点搜}cout << ans << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/70091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

led+串口代码 重定向 STM32F030F4P6 fgetc multiply defined错误

led串口 重定向 STM32F030F4P6 重定向报错 axf: Error: L6200E: Symbol fgetc multiply defined (by usart.o and main.o). 把 #include "stdio.h" int fputc(int ch, FILE *fp) {HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);return ch; }int f…

Letsencrypt+certbot为域名免费配置ssl

1、基础概念 Let’s Encrypt 是一个提供免费 SSL/TLS 证书的认证机构&#xff0c;它的目标是让互联网上的通信更加安全&#xff0c;特别是普及 HTTPS。通过 Let’s Encrypt 提供的证书&#xff0c;网站可以使用加密连接&#xff0c;保护用户的数据传输。 Certbot 是一个由电子…

BSD协议栈:UDP发送

BSD实现 在BSD中UDP头部数据结构如下&#xff1a; /** Udp protocol header.* Per RFC 768, September, 1981.*/ struct udphdr {u_short uh_sport; /* source port */u_short uh_dport; /* destination port */short uh_ulen; /* udp length */u_short uh_sum; /* udp …

QT (四)模型/视图 QFileSystemModel,QStringListModel,QStandardItemModel

思考&#xff1a;QTableWidget 在某种程度上可以等价为QStandardItemModel&#xff0c;同理&#xff0c;其他的功能也有类似的等价&#xff0c;但是以当前的QTableWidget 和QStandardItemModel为例的话&#xff0c;两者都是用于实现建立表格的相关组件&#xff0c;只不过QStand…

Unity热更新方案HybridCLR+YooAsset,从零开始,保姆级教程,纯c#开发热更

文章目录&#xff1a; 一、前言二、创建空工程三、接入HybridCLR四、接入YooAsset五、搭建本地资源服务器Nginx六、实战七、最后八、后记 一、前言 unity热更有很多方案&#xff0c;各种lua热更&#xff0c;ILRuntime等&#xff0c;这里介绍的是YooAssetHybridCLR的热更方案&a…

Zabbix——监控Windows下某个文件夹的所有文件变化

获取所有的指定路径下的文件列表 以D:\Apps这个文件夹下的所有文件为例&#xff0c;我需要找到这个文件夹里面的子文件夹中的所有文件&#xff0c;但是排除backup这个文件夹&#xff0c;下面我们来看怎么操作 在scripts目录下创建check_file_in_D_Apps.ps1文件&#xff0c;内容…

C++ Primer 函数匹配

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

java八股文-mysql

1. 索引 1.1 什么是索引 索引(index)是帮助Mysql高效获取数据的数据结构(有序).提高数据的检索效率,降低数据库的IO成本(不需要全表扫描).通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗. 1.2 mysql索引使用的B树? 1. 没有使用二叉树&#xff0c;最坏情况o&…

Docker拉不下来镜像问题解决法案

打开docker的设置界面 配置如下&#xff1a; vi /etc/docker/daemon.json {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-mirrors": ["…

春招项目=图床+ k8s 控制台(唬人专用)

1. 春招伊始 马上要春招了&#xff0c;一个大气的项目&#xff08;冲击波项目&#xff09;直观重要&#xff0c;虽然大家都说基础很重要&#xff0c;但是一个足够新颖的项目完全可以把你的简历添加一个足够闪亮的点。 这就不得不推荐下我的 k8s 图床了&#xff0c;去年折腾快…

wordpress主题插件开发中高频使用的38个函数

核心模板函数 get_header()/get_footer()/get_sidebar() – 加载模板部件 the_title()/the_content()/the_excerpt() – 显示文章标题、内容、摘要 the_post() – 循环中获取文章数据 bloginfo(‘url’) – 获取站点URL wp_head()/wp_footer() – 输出头部/尾部代码 wp_n…

vue点击左边导航,右边出现页面步骤

vue点击左边导航&#xff0c;右边出现页面 步骤 一定要import不然会出错 index.js Course作为Homeview子路由 Homeview加入<Routerview> 点击跳转<RouterLink to> 父Homeview中有RouterView&#xff08;路由出口&#xff0c;跳转至相应路径&#xff09;和Router…

阿里云视频点播,基于thinkphp8上传视频

前端参考官方示例(jQuery版) <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>阿里云 JavaScript上传SDK Demo (使用jquery)</title><script src"__STATIC__/jquery.min.js"></script><sc…

力扣 66.加一 (Java实现)

题目分析 给定一个数组&#xff0c;可以组成一个数字&#xff0c;将数字加一后&#xff0c;返回新数组 思路分析 首先跟着题目思路走&#xff0c;将数组按位*10可以得到数字&#xff0c;再加一&#xff0c;加一后按位%10&#xff0c;可以得到新的数组。但是此处数字会过大&…

[特殊字符] 用Rust重塑Web开发速度极限:Hyperlane框架——开启高性能服务的「光年时代」[特殊字符]

&#x1f525; 每秒百万级请求&#xff1f;Rust超新星Hyperlane框架让Web开发突破性能次元壁&#xff01;&#x1f525; &#x1f31f; 颠覆性技术亮点&#xff1a;为何全球顶尖工程师正疯狂迁移至Hyperlane&#xff1f; ⚡️ 「速度即正义」&#xff1a;重新定义Web性能天花…

DeepSeek与ChatGPT:AI语言模型的全面对决

DeepSeek与ChatGPT&#xff1a;AI语言模型的全面对决 引言&#xff1a;AI 语言模型的时代浪潮一、认识 DeepSeek 与 ChatGPT&#xff08;一&#xff09;DeepSeek&#xff1a;国产新星的崛起&#xff08;二&#xff09;ChatGPT&#xff1a;AI 界的开拓者 二、DeepSeek 与 ChatGP…

visutal studio 2022使用qcustomplot基础教程

编译 下载&#xff0c;2.1.1版支持到Qt6.4 。 拷贝qcustomplot.h和qcustomplot.cpp到项目源目录&#xff08;Qt project&#xff09;。 在msvc中将它俩加入项目中。 使用Qt6.8&#xff0c;需要修改两处代码&#xff1a; L6779 # if QT_VERSION > QT_VERSION_CHECK(5, 2, …

联想笔记本电脑摄像头灯亮,但没有画面怎么解决,

联想小新电脑&#xff0c;遇到电脑黑屏。 解决方法&#xff0c;搜索打开任务管理器 打开联想管家的路径 打开BatterySetting.exe 程序 然后右下角会弹出一个东西&#xff0c;关闭这个摄像头的隐私模式。就可以打开了 就可以了

蓝桥杯单片机大模板(西风)

#include <REGX52.H> #include "Key.h" #include "Seg.h" //变量声明区 unsigned char Key_Val,Key_Down,Key_Old;//按键扫描专用变量 unsigned char Key_Slow_Down;//按键减速专用变量 10ms unsigned int Seg_Slow_Down;//按键扫描专用变量 500ms …

ubuntu服务器 如何配置安全加固措施

下面提供一个更详细、一步步的服务器安全加固指南&#xff0c;适合新手操作。我们将从 Fail2Ban、SSH&#xff08;密钥认证及端口更改&#xff09;、Nginx 速率限制和日志轮转四个方面进行优化&#xff0c;同时补充一些额外的安全建议。 新的服务器&#xff0c;通常我们会创建一…