当前位置: 首页 > news >正文

【算法模板】图论:Tarjan算法求割边割点

概念

割边(Bridge 或 Cut Edge)

定义

  • 在一个无向连通图中,如果删除某条边后,图不再连通(即任意两点之间不能相互到达),则称该边为割边。割边也被称为桥,因为它像桥梁一样连接着图的两部分,一旦移除,这两部分就被隔断了。

特性

  • 割边只存在于无向连通图中。
  • 删除割边会导致图的连通分量数量增加。
  • 割边是图中的一个“薄弱环节”,对于图的连通性有重要影响。

边双连通分量(Edge Biconnected Component)

  • 定义
    • 一个无向图的边双连通分量是一个极大连通子图,删除任意一条边后,图仍然连通。
    • 类似于点双连通分量,边双连通分量中的任意两个节点之间都有两条不相交的边(即,如果删除一条边,仍然可以保持连通)。
  • 性质
    • 边双连通分量的每个分量都是连通的。
    • 边双连通分量可用于分析图中冗余的边以及提高图的可靠性。

割点(Articulation Point 或 Cut Vertex)

定义

  • 在一个无向连通图中,如果删除了某个顶点后,图不再连通(即任意两点之间不能相互到达),则称该顶点为割点。割点也被称为关节点,因为它像关节一样连接着图的不同部分,一旦移除,这些部分就被分开了。

特性

  • 割点同样只存在于无向连通图中。
  • 删除割点会导致图的连通分量数量增加或减少(具体取决于割点的位置和图的结构)。
  • 割点是图中的一个重要节点,对于维护图的连通性至关重要。

点双连通分量(Vertex Biconnected Component)

  • 定义

    • 一个无向图的点双连通分量是一个极大连通子图,删除任意一个节点后,图仍然连通。
    • 换句话说,点双连通分量中的任意两个节点之间都有两条不相交的路径(即,如果删除一个节点,仍然可以保持连通)。
  • 性质

    • 点双连通分量的每个分量都是连通的。
    • 点双连通分量可用来检测图中的割点(即删除后会增加连通分量的节点)。

割点与割边的关系

  • 存在割点时必有割边:如果一个节点是割点,那么至少存在一条通过该节点的割边。删除割点会导致图分裂为多个部分,每个部分之间至少存在一条割边。

  • 割边连接的节点可能是割点:割边的两个端点节点至少有一个可能是割点。特别是在边的两个端点是不同的双连通分量时,这两个节点通常是割点。

  • 独立的关系:虽然割点和割边紧密相关,但它们也可以独立存在。一个图可以有割点而没有割边,或者有割边而没有割点。例如,星型图的中心节点是割点,但星型图的每条边都是割边。

Tarjan算法

算法思想

  1. DFS 访问顺序: 每个节点都有一个 dfn 值,表示节点在 DFS 中被访问的时间戳(第几个被访问)。同时,还有一个 low 值,表示节点能通过回边或子节点到达的最早访问的节点的时间戳。
  2. 割点判定
    • 对于根节点,如果它有两个或两个以上的子树,则它是割点。
    • 对于非根节点,如果某个子节点的 low 值不小于该节点的 dfn 值,则该节点是割点。
  3. 割边判定: 如果某个节点 u 和它的子节点 v 之间的边满足 low[v] > dfn[u],则边 (u, v) 是割边。

算法步骤

  1. 初始化 dfnlow 数组,初始值为 -1,表示未被访问。初始化一个时间戳变量 time 为 0。
  2. 使用 DFS 遍历图,对于每个未被访问的节点调用 DFS 函数。
  3. 在 DFS 函数中:
    • 设置当前节点的 dfnlow 值为当前时间戳,并将时间戳加 1。
    • 对于每个邻接节点,如果该邻接节点未被访问,递归调用 DFS 函数,更新当前节点的 low 值。
    • 如果邻接节点已被访问且不是父节点,更新当前节点的 low 值为邻接节点的 dfn 值。
    • 在返回时,根据 low 值判断是否是割点或割边。
  4. 记录所有割点和割边。

算法模板

// 求解割点的函数
vector<int> findCutPoints(const vector<vector<int>> &graph) {int n = graph.size();vector<int> cutPoints; // 存储割点vector<int> dfn(n, -1), low(n), parent(n, -1); // 初始化 dfn, low, parent 数组vector<bool> visited(n, false); // 记录节点是否已被访问int time = 0; // 时间戳// DFS 函数function<void(int)> dfs = [&](int u) {visited[u] = true; // 标记节点 u 为已访问dfn[u] = low[u] = time++; // 设置 dfn 和 low 值int childCount = 0; // 子节点数量bool isCutPoint = false; // 是否为割点// 遍历邻接节点for (int v : graph[u]) {if (!visited[v]) { // 如果 v 未被访问parent[v] = u; // 设置 v 的父节点为 udfs(v); // 递归调用 DFSchildCount++; // 增加子节点数量// 更新 low[u]low[u] = min(low[u], low[v]);// 判断是否为割点if (parent[u] == -1 && childCount > 1) { // 根节点且有两个以上子树isCutPoint = true;}if (parent[u] != -1 && low[v] >= dfn[u]) { // 非根节点且满足条件isCutPoint = true;}} else if (v != parent[u]) { // 如果 v 已被访问且不是父节点low[u] = min(low[u], dfn[v]); // 更新 low[u]}}// 如果是割点,加入结果if (isCutPoint) {cutPoints.push_back(u);}};// 遍历每个节点,进行 DFSfor (int i = 0; i < n; ++i) {if (!visited[i]) {dfs(i);}}return cutPoints; // 返回割点列表
}// 求解割边的函数
vector<pair<int, int>> findBridges(const vector<vector<int>> &graph) {int n = graph.size();vector<pair<int, int>> bridges; // 存储割边vector<int> dfn(n, -1), low(n), parent(n, -1); // 初始化 dfn, low, parent 数组vector<bool> visited(n, false); // 记录节点是否已被访问int time = 0; // 时间戳// DFS 函数function<void(int)> dfs = [&](int u) {visited[u] = true; // 标记节点 u 为已访问dfn[u] = low[u] = time++; // 设置 dfn 和 low 值// 遍历邻接节点for (int v : graph[u]) {if (!visited[v]) { // 如果 v 未被访问parent[v] = u; // 设置 v 的父节点为 udfs(v); // 递归调用 DFS// 更新 low[u]low[u] = min(low[u], low[v]);// 判断是否为割边if (low[v] > dfn[u]) {bridges.push_back({u, v}); // 记录割边}} else if (v != parent[u]) { // 如果 v 已被访问且不是父节点low[u] = min(low[u], dfn[v]); // 更新 low[u]}}};// 遍历每个节点,进行 DFSfor (int i = 0; i < n; ++i) {if (!visited[i]) {dfs(i);}}return bridges; // 返回割边列表
}

例题

P3388 【模板】割点(割顶)
给出一个 n 个点,m 条边的无向图,求图的割点。

#include <bits/stdc++.h>
using namespace std;
vector<int> findCutPoints(const vector<vector<int>> &graph);
signed main() {int n,m;cin>>n>>m;vector<vector<int>> G(n+1);while(m--){int u,v;cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}vector<int> CutPoints=findCutPoints(G);cout<<CutPoints.size()<<endl;sort(CutPoints.begin(),CutPoints.end());for(int p:CutPoints)cout<<p<<' ';return 0;
}

P1656 炸铁路
将军uim需要选择一条铁路进行炸毁,以使得B国的物流系统瘫痪,导致至少存在两个城市无法通过铁路相互到达。要选择的铁路被称为“关键铁路”。

#include <bits/stdc++.h>
using namespace std;
vector<pair<int, int>> findBridges(const vector<vector<int>> &graph);
signed main() {int n,m;cin>>n>>m;vector<vector<int>> G(n+1);while(m--){int u,v;cin>>u>>v;G[u].push_back(v);G[v].push_back(u);}vector<pair<int, int>> Bridges=findBridges(G);for(auto&[x,y]:Bridges)if(x>y)swap(x,y);sort(Bridges.begin(),Bridges.end());for(auto[x,y]:Bridges)cout<<x<<' '<<y<<endl;return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • datawind可视化查询-计数count(xxx)函数
  • Brave浏览器:开启隐私保护新时代
  • 按照指定格式打印pprint()
  • 自动化测试面试题
  • LeetCode459 重复的子字符串
  • 按xls标签替换docx及xls内容
  • docker-compose笔记
  • Scrapy入门篇
  • 小米账号移除工具箱 | 移除MXTGT工具箱
  • IO流学习总结
  • 定时任务-xxl-job
  • Rabbitmq的死信队列与如何利用死信队列实现延迟队列
  • gitlab-runner /var/run/docker.sock connect permission denied
  • 【Wiki: 使用 netsh wlan show networks mode=bssid | findstr /R /C:“信号“ /C:“频道“ 命令】
  • 基于Python的Bilibili视频信息分析与可视化
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • Angular 4.x 动态创建组件
  • HashMap ConcurrentHashMap
  • HTTP中GET与POST的区别 99%的错误认识
  • jQuery(一)
  • vagrant 添加本地 box 安装 laravel homestead
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • XML已死 ?
  • ------- 计算机网络基础
  • 讲清楚之javascript作用域
  • 坑!为什么View.startAnimation不起作用?
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • ​2020 年大前端技术趋势解读
  • (1)无线电失控保护(二)
  • (11)MSP430F5529 定时器B
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (SpringBoot)第七章:SpringBoot日志文件
  • (二)springcloud实战之config配置中心
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (四)库存超卖案例实战——优化redis分布式锁
  • (转) Android中ViewStub组件使用
  • (转)母版页和相对路径
  • .a文件和.so文件
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .NET/C#⾯试题汇总系列:集合、异常、泛型、LINQ、委托、EF!(完整版)
  • .Net接口调试与案例
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • .Net中的集合
  • .NET中使用Redis (二)
  • @SuppressLint(NewApi)和@TargetApi()的区别
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [20171101]rman to destination.txt
  • [Android Pro] AndroidX重构和映射
  • [Android] Upload package to device fails #2720
  • [android] 练习PopupWindow实现对话框
  • [CC2642R1][VSCODE+Embedded IDE+IAR Build+Cortex-Debug] TI CC2642R1基于VsCode的开发环境