强连通分量

强连通分量

强连通定义

有向图 G G G 的强连通是指 G G G 中任意两个节点都可以直接或间接到达。

下方两幅图都是强连通。一个特殊一点,任意两点都可以直接到达;一个则是最常见的强连通图。

  • 特殊强连通图,任意两点都可以直接到达

  • 常见的强连通图,即一个环

强连通分量

强连通分量,简称 S C C SCC SCC,是在一个有向图中最大的强连通子图。

  • 图中加粗的点组成的子图即为此图的强连通分量

dfs 生成树的边

dfs 遍历过程就不多说了,这是图论基本能力。

dfs 深搜后,会出现四种不同情况的边,如下:

  • 树边:由 dfs 自然搜索到的边,组成一棵树(不一定是最小/大生成树)。
  • 返祖边(回边):由一个节点指向前面已经遍历过的祖先节点的边。
  • 横叉边:指向了一个访问过但不是当前节点的祖先的边。
  • 前向边:指向了目前未遍历到的节点,但以后会遍历到的节点的边。

例如,下图即为一张图 G G G

不难看出,dfs 生成树长这样:

对比一下,如下:

黑边为树边,是正常深搜而来的。

红边即为返祖边,因为它指向了当前节点 7 7 7 的祖先。

蓝边即为横叉边,因为它指向了当前生成树的另一个节点,但不是当前节点 9 9 9 的祖先。

绿边即为前向边,指向了还未加入生成树的节点。

Tarjan 算法

Tarjan 算法是用来求解 S C C SCC SCC 的著名算法,可以在线性时间复杂度完成统计 S C C SCC SCC 的任务。

思路

若节点 u u u S C C SCC SCC 在搜索树中访问到的第一个节点,那么 S C C SCC SCC 就肯定是一个以 u u u 为根节点的子树,我们称 u u u 为这个 S C C SCC SCC 的根。

Tarjan 算法基本思路为把每个 S C C SCC SCC 都看作搜索树的一个子树,将其节点一个个保存。

对于两个节点 u u u v v v,若 u u u v v v 的祖先,且 v v v 有一条返祖边能指向 u u u,则 u u u v v v 形成了环,属于一个 S C C SCC SCC。从 u u u v v v 一路上遇到的所有点也属于这一 S C C SCC SCC 中的点,边也为 S C C SCC SCC 中边。

步骤

每次遍历到一个节点 u u u,需要统计一下信息:

  • dfn[u],即 u u u 的时间戳(第几个被访问到的)。
  • low[u] u u u 属于的那个 S C C SCC SCCdfn 最小的时间戳。

初始化时,dfn[u]=low[u]=++tottot 为时间戳。

既然 dfs 是一种递归的算法,不妨用栈来存节点信息。

每次搜到一个节点都将其入栈,有出度则沿着出度遍历。

上文说到,dfs 搜索会搜到 4 4 4 种边,那么我们该如何解决 4 4 4 种边呢?

  • 树边:正常搜
  • 返祖边:更新当前的 low
  • 横叉边:无视,没用
  • 前向边:无视,没用

每次搜完子树都需要更新 u u ulow 值,若 low[u]==dfn[u],则 u u u 为这个 S C C SCC SCC 的根节点,因为没有比他时间戳更小的了(回溯完之后)。

例题:福州一中OJ P2110 求有向图的强连通分量

AC Code:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5,MAXM=8e5+5;
struct EDGE{
	int to,pre;
}edge[MAXM<<1];
int head[MAXN],cnt_edge,tot,t;
int n,m,op;
//链式前向星存图 
void add(int from,int to)
{
	edge[++cnt_edge].to=to;
	edge[cnt_edge].pre=head[from];
	head[from]=cnt_edge;
	return;
}
int dfn[MAXN],low[MAXN];
stack<int> st;
bool vis[MAXN];
int cnt_ans,cnt_t,maxn;
void dfs(int u)//目标是统计maxn 
{
	dfn[u]=low[u]=++tot;//时间戳和子树最小时间戳 
	st.push(u);
	vis[u]=true;
	for(int i=head[u];i;i=edge[i].pre)
	{
		if(!dfn[edge[i].to])
		{
			dfs(edge[i].to);
			low[u]=min(low[u],low[edge[i].to]);//更新 
		}
		else
			if(vis[edge[i].to])//返祖边,注意是vis,不是dfn(有可能是横叉边) 
				low[u]=min(low[u],dfn[edge[i].to]);
	}
	if(low[u]==dfn[u])//是SCC的根节点 
	{
		cnt_t=0;//统计SCC节点个数 
		do{//记得是先做在判断 
			vis[t=st.top()]=false;//比u后入栈的都是SCC的子节点 
			st.pop();
			cnt_t++;
		}while(u!=t);
		maxn=max(maxn,cnt_t);
	}
	return;
}
void dfs2(int u)//与dfs大同小异,目标是计算有多少个强连通子图 
{
	dfn[u]=low[u]=++tot;
	st.push(u);
	vis[u]=true;
	for(int i=head[u];i;i=edge[i].pre)
	{
		if(!dfn[edge[i].to])
		{
			dfs2(edge[i].to);
			low[u]=min(low[u],low[edge[i].to]);
		}
		else
			if(vis[edge[i].to])
				low[u]=min(low[u],dfn[edge[i].to]);
	}
	if(low[u]==dfn[u])
	{
		cnt_t=0;
		do{
			vis[t=st.top()]=false;
			st.pop();
			cnt_t++;
		}while(u!=t);
		if(cnt_t==maxn)
			cnt_ans++;
	}
	return;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&op);
		while(op--)
		{
			scanf("%d",&t);
			add(i,t);
		}
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			dfs(i);
	//初始化 
	while(!st.empty())
		st.pop();
	for(int i=1;i<=n;i++)
	{
		dfn[i]=0;
		low[i]=0;
		vis[i]=false;
	}
	//初始化 
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			dfs2(i);
	printf("%d %d\n",maxn,cnt_ans);
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机启动失败 请进行修复 关闭hyper-v

场景 win11开启夜神模拟器时弹出此提示。点击关闭hyper-v并重启电脑后仍然不行。 解决方法 关闭 Windows安全中心 的 内存完整性 后重启电脑恢复正常。 补充 由于我这里除了会用到夜神模拟器&#xff0c;还会用到docker&#xff0c;而docker又依赖hyper-v&#xff0c;不…

YOLOv5初学者问题——用自己的模型预测图片不画框

如题&#xff0c;我在用自己的数据集训练权重模型的时候&#xff0c;在训练完成输出的yolov5-v5.0\runs\train\exp2目录下可以看到&#xff0c;在训练测试的时候是有输出描框的。 但是当我引用训练好的best.fangpt去进行预测的时候&#xff0c; 程序输出的图片并没有描框。根据…

【小白教学】-- 安装Ubuntu-20.04系统

下载 Ubuntu-20.04 镜像 具体如何下载镜像&#xff0c;请移驾我上一篇文章。使用清华大学开源镜像站下载。https://zhuanlan.zhihu.com/p/706444837 制作 Ubuntu-20.04 系统盘 安装软件 UltralSO 开始制作系统盘 第一步&#xff0c;插入一个 u 盘&#xff0c;启动软件&#x…

PO模式登录测试

项目实践 登陆项目测试 get_driver import page from selenium import webdriverclass GetDriver:driver Noneclassmethoddef get_driver(cls):if cls.driver is None:cls.driver webdriver.Edge()cls.driver.maximize_window()cls.driver.get(page.url)return cls.drivercl…

关于批量采集1688商品主图及链接的方式:软件采集/1688官方API接口数据采集

关于批量采集&#xff0c;我们通常用到的是软件 采集&#xff0c;或者通过1688官方API数据采集的形式&#xff1a;用户输入一组1688商品ID&#xff0c;一行一个&#xff0c;流程会自动逐个打开对应的1688商品详情页&#xff0c;采集主图的所有链接。 结果保存为表格的一行&…

Linux运维之管道符、重定向与环境变量

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、输入输出重定向 二、管道命令符 三、命令行的通配符 四、常用的转义字符 五、重要的环境变量 致谢 一、输入输出重定向 输入重定向是…

【Python+微信小程序】学生考勤签到系统(已开源)

1. 简介 &#x1f61d; 这个项目是一款基于微信小程序和Flask框架开发的应用&#xff0c;旨在帮助学校管理学生的考勤和课程信息。系统通过集成数据库管理、API开发以及前后端交互&#xff0c;实现了便捷的学生考勤记录、课程表管理和教师交互功能。其主要特点包括&#xff1a…

程序化交易广告及其应用

什么是程序化交易广告&#xff1f; 程序化交易广告是以实时竞价技术即RTB&#xff08;real-time bidding&#xff09;为核心的广告交易方式。说到这里&#xff0c;你可能会有疑问&#xff1a;像百度搜索关键词广告还有百度网盟的广告&#xff0c;不也是CPC实时竞价的吗&#x…

Python学习笔记22:进阶篇(十一)常见标准库使用之访问互联网功能urllib模块的学习使用,requests库和aiohttp库了解

前言 本文是根据python官方教程中标准库模块的介绍&#xff0c;自己查询资料并整理&#xff0c;编写代码示例做出的学习笔记。 根据模块知识&#xff0c;一次讲解单个或者多个模块的内容。 教程链接&#xff1a;https://docs.python.org/zh-cn/3/tutorial/index.html 互联网访…

【基于R语言群体遗传学】-5-扩展到两个以上等位基因及多基因位点

我们现在继续对于群体遗传学进行统计建模&#xff0c;书接上回&#xff0c;我们讨论了孤雌生殖的物种违反哈代温伯格遗传比例的例子&#xff0c;那我们现在来看多于两个等位基因的情况的计算。 如果没有看过之前文章的同学&#xff0c;可以先去看一下之前的文章&#xff1a; …

1.2 ROS2安装

1.2.1 安装ROS2 整体而言&#xff0c;ROS2的安装步骤不算复杂&#xff0c;大致步骤如下&#xff1a; 准备1&#xff1a;设置语言环境&#xff1b;准备2&#xff1a;启动Ubuntu universe存储库&#xff1b;设置软件源&#xff1b;安装ROS2&#xff1b;配置环境。 请注意&…

多模态图像生成的突破:Image Anything一种无需训练的智能框架

多模态图像生成是内容创作领域的热点技术&#xff0c;尤其在媒体、艺术和元宇宙等领域。该技术旨在模拟人类的想象力&#xff0c;将视觉、文本和音频等多种模态属性相关联&#xff0c;以生成图像。早期的方法主要侧重于单一模态输入的图像生成&#xff0c;例如基于图像、文本或…

240703_昇思学习打卡-Day15-K近邻算法实现红酒聚类

KNN(K近邻)算法实现红酒聚类 K近邻算法&#xff0c;是有监督学习中的分类算法&#xff0c;可以用于分类和回归&#xff0c;本篇主要讲解其在分类上的用途。 文章目录 KNN(K近邻)算法实现红酒聚类算法原理数据下载数据读取与处理模型构建--计算距离模型预测 算法原理 KNN算法虽…

Mac单机游戏推荐:星际争霸母巢之战 for Mac v1.16.1汉化版

星际争霸母巢之战 for Mac是一款深受玩家的即时战略游戏&#xff0c;延续了原版《星际争霸》的剧情&#xff0c;并加入了新的游戏单位、科技、地图样式、背景音乐及平衡性调整。《星际争霸》与其它的即时战略类型游戏。 下载地址&#xff1a;点击下载 与原作相同&#xff0c;《…

一图胜千言|用Python搞定统计结果展示!

分享一份原创Python可视化教程&#xff1a;530张图形8000行代码&#xff0c;轻松搞定统计结果展示&#xff0c;部分如下&#xff0c; 每类图表包含详细代码详细代码注释&#xff0c;多达8000行代码&#xff0c;例如&#xff0c; 如何加入学习&#xff1f; &#x1f447;&#…

免费分享:2022年全国地铁站点数据(附下载方法)

数据简介 2022年全国地铁站点数据不仅反应我国城市交通网络的日益完善&#xff0c;也为城市规划、公共交通优化、商业布局、应急响应及智慧城市建设提供了宝贵的数据支持与参考&#xff0c;助力城市发展与居民生活质量的全面提升。 数据属性 数据名称&#xff1a;全国地铁站点…

Java同步包装器

通过 Collections.synchronizedList() 方法将一个普通的 ArrayList 包装成了线程安全的 List&#xff1a; import java.util.*;public class SynchronizedWrapperExample {public static void main(String[] args) {// 创建一个非线程安全的 ArrayListList<String> list…

python gdal 压缩栅格数据

1 压缩方法LZW 使用 LZW&#xff08;Lempel-Ziv-Welch&#xff09;&#xff0c;主要对图像数据压缩&#xff0c;可逆 2 代码 函数gdal_translate()&#xff1a;转换栅格的不同格式 我们使用的数据是GTiff格式的数据 GTiff – GeoTIFF File Format — GDAL documentation 参…

MySQL安装与环境配置

1.打开安装程序 2.默认配置&#xff0c;如下二三图 3.配置密码 4.等待安装完毕 5.检查 6.配置环境变量 7.从控制台登录检测

STM32F1+HAL库+FreeTOTS学习4——任务挂起与恢复

STM32F1HAL库FreeTOTS学习4——任务挂起与恢复 任务挂起和恢复的API介绍代码实现 上一期我们学习了FreeRTOS中任务创建的两种方法&#xff0c;这一期我们学习任务的挂起和恢复。 任务挂起和恢复的API介绍 在 &#xff1a;STM32F1HAL库FreeTOTS学习1——FreeRTOS入门 的学习中&…