首页 > 其他分享 >强连通分量学习笔记+杂题

强连通分量学习笔记+杂题

时间:2024-10-30 20:20:12浏览次数:6  
标签:连通 结点 笔记 dfn low SCC 杂题 分量

图论系列:

前言:

僕は
明快さ故にアイロニー
優柔不断なフォローミー
後悔後悔夜の果て

相关题单:戳我

一.强连通分量相关定义

基本摘自oi wiki ,相关定义还是需要了解。(实际就是搬了个oi wiki)
强连通分量主要在研究有向图可达性,针对的图类型为有向弱联通图。

1.强连通定义

强连通:对于有向图的两点 \(u,v\),它们互相可达。

强连通图:满足图内任意两点强连通的有向图。

强连通分量(Strongly Connected Components,SCC):极大的强连通子图。(也就是说在同一强连通分量的任意两点都是互相可达的,于是在关心可达性时,同一强连通分量内的点等价)。

一般使用 Tarjan 求强连通分量。

2.有向图 DFS 树

对于一张有向图,对其跑 DFS ,不经过已经遍历过的点的前提下,遍历过程可以看似一颗树,称作有向图 DFS 树,单从某一点出发 DFS,不一定能访问到图上的所有结点,所以一般有向图 DFS 树是一个森林。

形成若干棵 DFS 树后,在研究强连通性时,它们之间相互独立。对于任意强连通的两点 \(u,v\) ,第一次访问它们所在的强连通分量的 DFS 树一定同时包含 \(u,v\)。

我们一般用时间戳 \(dfn_i\) 表示遍历到点 \(i\) 的时间,可以得到遍历到各个点的先后顺序。

不同于一般的树,除了树边还有其他类型的边,如图,有向图的 DFS 生成树主要有 4 种边(不一定全部出现):

树边:就是遍历的时候经过的边,示意图中以黑色边表示,,在每次搜索找到一个还没有访问过的结点的时候就形成了一条树边。

反祖边:示意图中以红色边表示(即 \(7 \rightarrow 1\) ),也被叫做回边,即指向祖先结点的边。

横叉边:示意图中以蓝色边表示(即 \(9 \rightarrow 7\) ),它主要是在搜索的时候遇到了一个已经访问过的结点,但是这个结点并不是当前结点的祖先。

前向边:示意图中以绿色边表示(即 \(3 \rightarrow 6\) ),它是在搜索的时候遇到子树中的结点的时候形成的。

由于强连通分量主要和性有关,现在讨论各类边对可达性的影响:

对于前向边 \(u \to v\) ,\(u\) 本来通过树边就可到达 \(v\),所以对可达性没有影响。

对于反祖边 \(v \to u\) ,\(u\) 本身可以通过树边到达 \(u \to v\) 路径上的所有点 ,而这条返祖边使得所有在 \(u \to v\) 路径上的点可以到达点 \(u\),于是 \(u \to v\) 路径上的所有点就构成了一个强连通分量。多个返祖边结合可以形成更大更复杂的强连通结构。

对于横叉边,也可以使得时间戳减小。

通过讨论我们可以发现:

若 \(u,v\) 强连通,则 \(u,v\) 在树上路径上的所有点强连通。

强连通分量在有向图 DFS 树上弱连通。

3. Tarjan 求 SCC

Tarjan 算法基于对图进行 深度优先搜索。我们视每个连通分量为搜索树中的一棵子树,在搜索过程中,维护一个栈,每次把搜索树中尚未处理的节点加入栈中。时间复杂度是 \(O(n+m)\) 的。

在 Tarjan 算法中为每个结点 \(u\) 维护了以下几个变量:

\(dfn_u\) :就是在 DFS 树内的时间戳。(一个结点的子树内结点的 dfn 都大于该结点的 dfn 值。)

\(low_u\) :在 u 的子树中能够回溯到的最早的已经在栈中的结点。设以 \(u\) 为根的子树为 \(T_u\)。\(T_u\) 定义为以下结点的 \(dfn\) 的最小值:\(T_u\) 中的结点;从 \(T_u\) 通过一条不在搜索树上的边能到达的结点(要么是返祖边,要么是横叉边)。(一般初始化 \(low_u=dfn_u=++num\))

按照深度优先搜索算法搜索的次序对图中所有的结点进行搜索,维护每个结点的 dfn 与 low 变量,且让搜索到的结点入栈。每当找到一个强连通元素,就按照该元素包含结点数目让栈中元素出栈。在搜索过程中,对于结点 \(u\) 和与其相邻的结点 \(v\)( \(v\) 不是 \(u\) 的父节点)考虑 3 种情况:

\(v\) 未被访问:继续对 \(v\) 进行深度搜索。在回溯过程中,用 \(low_v\) 更新 \(low_u\)。因为存在从 \(u\) 到 \(v\) 的直接路径,所以 \(v\) 能够回溯到的已经在栈中的结点,\(u\) 也一定能够回溯到。
\(v\) 被访问过,已经在栈中:根据 low 值的定义,用 \(dfn_v\) 更新 \(low_u\)。
\(v\) 被访问过,已不在栈中:说明 \(v\) 已搜索完毕,其所在连通分量已被处理,所以不用对其做操作。

对于一个连通分量图,我们很容易想到,在该连通图中有且仅有一个 \(u\) 使得 \(dfn_u=low_u\)。该结点一定是在深度遍历的过程中,该连通分量中第一个被访问过的结点,因为它的 dfn 和 low 值最小,不会被该连通分量中的其他结点所影响。

因此,在回溯的过程中,判定 \(dfn_u=low_u\) 是否成立,如果成立,则栈中 \(u\) 及其上方的结点构成一个 SCC。

一些结论:

一个点属于一个 SCC。

SCC 缩点后,得到的新图不含环,否则环上所有结点对应的 SCC 可以形成一个更大的 SCC。这说明 SCC 缩点图是一张 DAG

对于两个 SCC ,\(S1\) 若可达 \(S2\) 则 \(S1\) 比 \(S2\) 后弹出栈。按弹出顺序写下所有 SCC,得到缩点 DAG 的反拓扑序。因此,按编号从大到小遍历 SCC,就是按拓扑序遍历缩点 DAG。

Tarjan 缩点代码

inline void dfs(int u)
{
	dfn[u]=low[u]=++num,vis[u]=1;//初始化,标记u在栈内
	s.push(u);//s是栈
	for(int i=head[u];i!=0;i=p[i].next)
	{
		int v=p[i].to;
		if(!dfn[v])//子树内没有遍历过的点
		{
			dfs(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v]) low[u]=min(low[u],dfn[v]);//不在子树内&没有处理过的点
	}
	if(low[u]==dfn[u])//是关键点
	{
		int x;++tot;//缩后点数+1
		while(1)
		{
			vis[(x=s.top())]=0;//出栈,已经操作,取消标记
			s.pop();
			if(x==u) break;//已经把栈内u上面的弹完了,退出
		}
	}
}

标签:连通,结点,笔记,dfn,low,SCC,杂题,分量
From: https://www.cnblogs.com/call-of-silence/p/18516535

相关文章

  • 【SQL】Hive/Spark SQL笔记之时间函数、环比/同比/时间比较计算
    获取当天:'${zdt.format("yyyy-MM-dd")}'//获取上月月末select'${zdt.lastMonth().format("yyyy-MM-dd")}'T-1上月末select'${zdt.addDay(-1).lastMonth().format("yyyyMMdd")}'1个小时前select'${zdt.addHour(-1)......
  • 读书笔记2
    6.交流交流又有技巧,与客户的交流更是如此,书中介绍了几个需要注重的方面:1.知道自己要说什么首先要自己组织好语言。围绕心中所想的框架展开阐述2.了解听众想要什么交流时双方的事,是双方希望通过交流来达到共识,所以我们需要知道听众想要听到什么,比如,你可以用以下方法展开:你想......
  • 程序员修炼之道阅读笔记
    读完《程序员修炼之道:从小工到专家》的第二章“注重实效的途径”,我收获颇丰。这一章详细阐述了实现注重实效编程的具体方法和途径。它强调了在编程过程中的各种细节和要点,为我们提供了切实可行的指导。其中,关于早期原型制作的内容让我印象深刻。通过快速构建原型,我们可以更早地......
  • 【机器人学导论】简明学习笔记1——概述
    主要参考学习资料:《机器人学导论(第4版)》JohnJ.Craig著台大机器人学之运动学——林沛群前置课程:博主目前只对线性代数和理论力学方面有一定基础,学习过程中遇到额外必要的前置知识我会做补充或者开辟新的知识笔记专栏笔记特点:简明扼要,对学习资料进行消化调整辅助理解码......
  • 程序员修炼之道:从小工到专家阅读笔记
    阅读《程序员修炼之道:从小工到专家》的第一章“注重实效的哲学”,让我深受启发。这一章强调了程序员应具备的务实态度和思维方式。它提醒我们,在编程的世界里,不能仅仅局限于技术的表面,而要深入理解问题的本质。实效不仅仅是关于写出能运行的代码,更是要写出可靠、易维护且能适应变......
  • Linux 常用命令笔记
    Linux命令行常用快捷键Ctrl+a:移到行首Ctrl+e:移到行尾ctrl+u:光标处往前删除ctrl+k:光标处往后删除Linux常用命令汇总vim:dd:删除游标所在的一整行(常用)网络相关命令汇总netstat:打印网络连接、路由表、接口统计、伪装连接和多播成员关系lsof:lsof(listopenfiles)是一个列出当......
  • Learn-前端-笔记-day05
    浮动div是块状元素,一个div都是独占一行,此时很多div元素在一排排列,就可以用到浮动,让竖着的盒子横着排列。浮动属性<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"......
  • Lyndon 理论学习笔记
    字符串,太深刻了/kk定义下标从1开始。\(+\)是字符串拼接。\(|s|\)表示\(s\)的长度。\(s_i\)表示\(s\)的第\(i\)个字符。\(s^k\)表示\(k\)个\(s\)拼接的结果。字符串间的大小关系用字典序比较。Lyndon串字符串\(s\)是Lyndon串当且仅当\(s\)小于其......
  • 10月30日记录(《代码大全》(第二版)精读笔记)
    《代码大全》中对于“代码质量”和“设计原则”的探讨深刻而全面,给我留下了深刻的印象。在当今快速发展的软件开发环境中,理解和应用这些概念对于提升开发效率和软件质量至关重要。首先,关于代码质量,麦克康奈尔强调了代码不仅需要正确实现功能,还必须具备良好的可读性和可维护性。代......
  • 小白江科大stm32笔记
    P5        GPIO输出·带FT的引脚可容忍5v电压·所有的GPIO都是在APB2外设总线上·每个GPIO总共有16个引脚,从0到15·32是32位单片机,寄存器有32位,但只有16个端口,所以只有低16位有端口下图为GPIO基本结构:  ·以下面GPIO电路图为例,左三为寄存器,中间为驱动器,右边为......