进阶图论

news/2025/4/3 2:55:19/文章来源:https://www.cnblogs.com/HEGVDV/p/18804885

进阶图论

I. 割点与桥

首先,我们得了解割点的含义

割点

对于一个无向图,如果把一个点删除后这个图的极大连通分量数增加了,那么这个点就是这个图的割点(又称割顶)。

通俗点说,就是连接两个或多个连通分量的公共点。

如何求割点呢,这里引用一个算法:Ttarjan

定义 dfn[i] 为 i 点DFS访问的顺序 , low[i] 为 i 点不经过其父亲能到达的最小的DFS序。

接着,我们开始DFS。

怎样判断割点呢, 对于一个顶点u,如果存在至少一个顶点 v (u的儿子),使得 low[v] > low[u] ,即不能回到祖先,那么u点为割点。(为什么自己想)

当然,此根据不适用搜索的起始点。(也是自己想)

更新low的代码:

if(!dfn[v])low[u] = min(low[u], low[v]);
else if(instack[i])low[u] = min(low[u], dfn[v]);

instack[i] 表示 i 点有没有被处理。u 是当前节点, v 是这个节点的所有邻居。

最后就是整体代码。

/*
洛谷 P3388 【模板】割点(割顶)
*/
#include <iostream>
#include <vector>
using namespace std;
int n, m;  // n:点数 m:边数
int dfn[100001], low[100001], idx, res;
// dfn:记录每个点的时间戳
// low:能不经过父亲到达最小的编号,idx:时间戳,res:答案数量
bool vis[100001], flag[100001];  // flag: 答案 vis:标记是否重复
vector<int> edge[100001];        // 存图用的void Tarjan(int u, int fa) {  // u 当前点的编号,fa 自己爸爸的编号vis[u] = true;              // 标记low[u] = dfn[u] = ++idx;    // 打上时间戳int child = 0;              // 每一个点儿子数量for (const auto &v : edge[u]) {  // 访问这个点的所有邻居 (C++11)if (!vis[v]) {child++;                       // 多了一个儿子Tarjan(v, u);                  // 继续low[u] = min(low[u], low[v]);  // 更新能到的最小节点编号if (fa != u && low[v] >= dfn[u] && !flag[u]) {  // 主要代码// 如果不是自己,且不通过父亲返回的最小点符合割点的要求,并且没有被标记过// 要求即为:删了父亲连不上去了,即为最多连到父亲flag[u] = true;res++;  // 记录答案}} else if (v != fa) {// 如果这个点不是自己的父亲,更新能到的最小节点编号low[u] = min(low[u], dfn[v]);}}// 主要代码,自己的话需要 2 个儿子才可以if (fa == u && child >= 2 && !flag[u]) {flag[u] = true;res++;  // 记录答案}
}int main() {cin >> n >> m;                  // 读入数据for (int i = 1; i <= m; i++) {  // 注意点是从 1 开始的int x, y;cin >> x >> y;edge[x].push_back(y);edge[y].push_back(x);}  // 使用 vector 存图for (int i = 1; i <= n; i++)  // 因为 Tarjan 图不一定连通if (!vis[i]) {idx = 0;       // 时间戳初始为 0Tarjan(i, i);  // 从第 i 个点开始,父亲为自己}cout << res << endl;for (int i = 1; i <= n; i++)if (flag[i]) cout << i << " ";  // 输出结果return 0;
}

割边(无重边)

和割点差不多,叫做桥(点换成边嘛)。不做解释。

更新的方法和割点差不多, 只需改一处low[v] > dfn[u],也就是不用判断根节点了。

下面代码实现了对 无重边 的无向图求割边,其中,当 isbridge[x] 为真时,(father[x],x) 为一条割边。

int low[MAXN], dfn[MAXN], idx;
bool isbridge[MAXN];
vector<int> G[MAXN];
int cnt_bridge;
int father[MAXN];void tarjan(int u, int fa) {father[u] = fa;low[u] = dfn[u] = ++idx;for (const auto &v : G[u]) {if (!dfn[v]) {tarjan(v, u);low[u] = min(low[u], low[v]);if (low[v] > dfn[u]) {isbridge[v] = true;++cnt_bridge;}} else if (v != fa) {low[u] = min(low[u], dfn[v]);}}
}

en就这么多。

圆方树

点双联通分量不是很好缩点,但是我们可以建立圆方树:每个点双建一个点,称之为方点,原图中每个点也建一个新点,称之为原点。每个方点与属于他的圆点连边,就构成了一棵树,称为圆方树。

图例

下面的图显示了一张图对应的点双和圆方树形态

imgimgimg

因为圆方树是基于割点的,所以只需要用类似求割点的方法构建圆方树。

void Tarjan(int u) {low[u] = dfn[u] = ++dfc;                // low 初始化为当前节点 dfnfor (int v : G[u]) {                    // 遍历 u 的相邻节点if (!dfn[v]) {                        // 如果未访问过Tarjan(v);                          // 递归low[u] = min(low[u], low[v])	  // 未访问的和 low 取 minif (low[v] == dfn[u])  			 // 标志着找到一个以 u 为根的点双连通分量++cnt;                 			 // 增加方点个数// 将点双中除了 u 的点退栈,并在圆方树中连边for (int x = 0; x != v; --tp) {x = stk[tp];T[cnt].push_back(x);T[x].push_back(cnt);}// 注意 u 自身也要连边(但不退栈)T[cnt].push_back(u);T[u].push_back(cnt);elselow[u] = min(low[u], dfn[v]);  // 已访问的和 dfn 取 min}
}

II.网络流

网络流是指一个特殊的有向图。

这个图的特殊点在于有两个特殊的点,源点和汇点,还有一个类似边权的容量

网络流满足以下限制

1.容量限制:对于每条边,流经该边的流量不得超过该边的容量

2.流守恒性:除源汇点外,任意结点 u的净流量为0.(也就是流入和流出的流量一样)

最大流

定义:

给定网络G及G上的流 f.

对于边(u, v),我们将其容量与流量之差称为剩余容量c(u,v)

我们将G中所有结点和剩余容量大于0的边构成的子图称为残量网络.

我们将G上一条从源点s到

Edmonds–Karp算法(简称EK)

对于每一个容量

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

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

相关文章

URAT协议自学笔记

定义 URAT(Universal Asynchronous Receiver/Transmitter)协议是一种通用异步收发传输协议,用于在两个设备之间进行串行数据通信。它将并行数据转换为串行数据进行传输,接收时再将串行数据转换为并行数据。 特点 异步通信:不需要共享时钟信号,通过波特率同步数据传输。 全…

解决手机每次复制都弹出google的nearby服务-分享到附近的设备

起因: 我的手机刷了国际版的系统,有google套件,每次复制都会唤起nearby附近分享这个组件,感觉很烦。 解决方法: 下载AppOps,这个软件能方便我们管理和监视应用权限。 使用Shizuku授权点击左上角,选择使用情况历史点击剪切板我们切出去, 在别的应用中进行一次复制,就可…

neo4j community教程

neo4j community最新版本安装教程(2025.1) 前言 ​ neo4j desktop以交互性好,体验好为优点。但是自24年中旬neo4j官网先后被墙和neo4j desktop联网打不开等诸多问题,本文作者建议使用community版本。 正文下载安装包neo4j官网,下载community最新版本(5.26.1)jdk-23下载(这…

代码随想录第四天 | Leecode 24. 两两交换链表、19.删除链表的倒数第N个节点、 面试题 02.07. 链表相交 、142.环形链表II

Leecode 24. 两两交换链表 题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/description/ 题目描述:思路:虚拟头节点,注意保存临时节点 题目解答: 循环结束的时候就是,当链表偶数个时cur->next->next=NULL,奇数个时cur->next=NULL。 Leecode 19. 删…

JpaSpecificationExecutor :Spring Data JPA 动态查询的核心扩展

JpaSpecificationExecutor 是 Spring Data JPA 动态查询的核心扩展,通过 Specification 机制提供灵活的类型安全查询,解决了传统 JPQL 或 Criteria API 的冗余问题,特别适合复杂业务场景下的动态数据访问需求。 核心设计目标: ​动态查询:允许运行时动态构建查询条件,避免…

四款高效报表软件推荐 助力企业数据可视化

概述 在数字化时代,企业和组织越来越依赖数据驱动决策,报表软件成为提高数据可视化能力、优化业务管理的关键工具。本文将为大家介绍四款功能强大的报表软件,帮助不同需求的企业找到合适的解决方案。 一、山海鲸报表 山海鲸报表是一款零代码的免费数据可视化和报表生成工具,…

AMDGPU对CUDA支持方式的分析

AMDGPU对CUDA支持方式的分析 实现方式是针对HIP API(Heterogeneous-Computing Interface for Portability)做CUDA的接口套壳,即将cuda的API接口作为标准接口,用AMD ROCm实现cuda的API(目的就是保证对外的API与CUDA完全相同),但实际调用 HIP+ROCm 的相关接口实现(即实际…

AMD GPU平台简单分析

AMD GPU平台简单分析 在启动docker的测试命令中,传入一个设备参数--device=/dev/kfd,转到DOCKER环境,kfd表示AMDGPU异构计算的GPU设备驱动(KMD)的设备节点,它是用户操作GPU的基础。AMD GPU设备驱动流程简单分析,如图1-19所示。图1-19 AMD GPU设备驱动流程简单分析 AMDKF…

Javascript - 3

Javascript - 3 背后的运行原理High levelGarbage-collectedjs引擎内部的算法,为了不被不必要的东西堵塞,会从计算机内存中 自动删除旧的、未使用的对象解释型的 / 即时编译的语言(为了更快做出的调整) interpreted or just-in-time compiled通过 解释器(Interpreter) 逐行…

GPU学习笔记

从引言中“Dennard Scaling”的失效开始,引入GPU出现的背景,又介绍了GPU的通用性,以及高并发、低延迟保证的高计算速度。随后,我们以最常见的CUDA为例,介绍了GPU编程的基础,SIMT与SIMD,编译链接的过程。最后,我们深入硬件层面,分为三步走,先用最简系统“run起来”,然…

信创邮箱了解一下

企业微信后台,邮箱模块突然多了 【信创邮箱】这模块,让我们了解一下信创邮箱。一、信创邮箱的定义 信创邮箱,全称“信息技术应用创新邮箱”,是指基于信创全生态打造的企业邮箱服务。它遵循“信息技术安全创新”(简称“信创”)原则,采用国内自主研发的技术和方案实现信息…

小了 60,500 倍,但更强;AI 的“深度诅咒”

作者:Ignacio de Gregorio图片来自 Unsplash 的 Bahnijit Barman几周前,我们看到 Anthropic 尝试训练 Claude 去通关宝可梦。模型是有点进展,但离真正通关还差得远。 但现在,一个独立的小团队用一个只有一千万参数的模型通关了宝可梦,比主流前沿 AI 模型小了几千倍。 举个…