每周算法:无向图的双连通分量

题目链接

冗余路径, Redundant Paths G

题目描述

为了从 F F F 个草场中的一个走到另一个,奶牛们有时不得不路过一些她们讨厌的可怕的树。

奶牛们已经厌倦了被迫走某一条路,所以她们想建一些新路,使每一对草场之间都会至少有两条相互分离的路径,这样她们就有多一些选择。

每对草场之间已经有至少一条路径。

给出所有 R R R 条双向路的描述,每条路连接了两个不同的草场,请计算最少的新建道路的数量,路径由若干道路首尾相连而成。

两条路径相互分离,是指两条路径没有一条重合的道路。

但是,两条分离的路径上可以有一些相同的草场。

对于同一对草场之间,可能已经有两条不同的道路,你也可以在它们之间再建一条道路,作为另一条不同的道路。

输入格式

1 1 1 行输入 F F F R R R

接下来 R R R 行,每行输入两个整数,表示两个草场,它们之间有一条道路。

输出格式

输出一个整数,表示最少的需要新建的道路数。

样例 #1

样例输入 #1

7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7

样例输出 #1

2

提示

【数据范围】

1 ≤ F ≤ 5000 1≤F≤5000 1F5000,
F − 1 ≤ R ≤ 10000 F−1≤R≤10000 F1R10000

【题目来源】

算法思想

根据题目描述,要在一个无向的连通图中,建一些新路,使每一对草场之间都会至少有两条相互分离的路径,计算最少的新建道路的数量。

先来分析一下测试样例,如下图所示。
在这里插入图片描述
其中蓝色虚线的边, ( 1 , 2 ) , ( 5 , 6 ) , ( 5 , 7 ) (1,2),(5,6),(5,7) (1,2),(5,6),(5,7),删除其中一条后,都会将图分裂成两个不相连通的子图,这样的边又被成为割边,或桥。

而绿色虚线中的子图 { 2 , 3 , 4 , 5 } \{2,3,4,5\} {2,3,4,5}之间都有两条“相互分离的路径”(不存在相同边的路径)。在这个子图中是不存在桥的,这样的子图又被成为边双连通分量

要解决这个问题之前,先来了解一下相关概念。

相关概念

割点

给定无向连通图 G = ( V , E ) G=(V,E) G=(V,E)
若对于 u ∈ V u\in V uV,从图中删去边节点 u u u以及所有与 u u u关联的边之后, G G G分裂成两个或两个以上不相连的子图,则称 u u u G G G割点

给定无向连通图 G = ( V , E ) G=(V,E) G=(V,E)
若对于 e ∈ E e\in E eE,从图中删去边 e e e之后, G G G分裂成两个不相连的子图,则称 e e e G G G割边

无向图的双连通分量

若一张无向连通图不存在割点,则成它为点双连通图。若一张无向连通图不存在桥,则称它为边双连通图。

无向图的极大点双连通子图被称为点双连通分量,记为 v-DCC \text{v-DCC} v-DCC,Vertex Double Connected Component;无向图的极大边双连通子图被称为边双连通分量,记为 e-DCC \text{e-DCC} e-DCC,Edge Double Connected Component。

Tarjan算法

Tarjan算法能够在线性时间内求出无向图的割点与桥,进一步可以求出无向图的双连通分量。

Tarjan算法基于无向图的深度优先遍历,并引入了时间戳的概念。

时间戳

在图的深度优先遍历过程中,按照每个节点第一次被访问的时间顺序,依次将 N N N个节点标记为 1 ∼ N 1\sim N 1N,该标记就被称为时间戳,记为 d f n [ u ] dfn[u] dfn[u]

搜索树

在无向连通图中任选一个节点出发进行深度优先遍历,每个点只访问一次。所有发生递归的边 ( u , v ) (u,v) (u,v),构成一棵树,被称为无向连通图的搜索树。如下图所示,其中节点和绿色的边构成了一棵搜索树。
在这里插入图片描述

追溯值

除了时间戳之外,Tarjan算法还引入了一个追溯值 l o w [ u ] low[u] low[u]。设子树 s u b t r e e ( u ) subtree(u) subtree(u)表示搜索树中以 u u u为根的子树。 l o w [ u ] low[u] low[u]表示为以下节点的时间戳的最小值:

  1. s u b t r e e ( u ) subtree(u) subtree(u)中的节点
  2. 通过 1 1 1条不在搜索树上的边,能够到达 s u b t r e e ( u ) subtree(u) subtree(u)的节点

在这里插入图片描述
如上图所示,其中节点编号为时间戳。 s u b t r e e ( 2 ) = { 2 , 3 , 4 , 5 } subtree(2)=\{2,3,4,5\} subtree(2)={2,3,4,5},由于节点 1 1 1通过不在搜索树上的边 1 → 5 1\to5 15能够到达 s u b t r e e ( 2 ) subtree(2) subtree(2),所以 l o w [ 2 ] = 1 low[2]=1 low[2]=1

为了计算 l o w [ u ] low[u] low[u],首先将low[u] = dfn[u] = ++timestamp,然后考虑从 u u u出发的每条边 ( u , v ) (u,v) (u,v)

  • 若在搜索树上, u u u v v v的父结点,则令 low[u] = min(low[u], low[v])
  • 若无向边 ( u , v ) (u,v) (u,v)不是搜索树上的边,则令 low[u] = min(low[u], dfn[v])

下图括号里的数值标注了每个节点的追溯值 l o w low low
在这里插入图片描述

割边(桥)判断法

无向边 ( u , v ) (u,v) (u,v)是桥,当且仅当搜索树上存在 u u u的一个子节点 v v v满足: d f n [ u ] < l o w [ v ] dfn[u]<low[v] dfn[u]<low[v]

根据定义, d f n [ u ] < l o w [ v ] dfn[u]<low[v] dfn[u]<low[v]说明从 s u b t r e e ( v ) subtree(v) subtree(v)出发,在不经过 ( u , v ) (u,v) (u,v)这条边的前提下,不管走那条边都无法到达 u u u或者比 u u u更早访问的节点。这样的话,若把 ( u , v ) (u,v) (u,v)删除,则 s u b t r e e ( v ) subtree(v) subtree(v)就形成了一个封闭的连通块,与节点 u u u没有边相连,图断开成立两部分。因此 ( u , v ) (u,v) (u,v)是割边(桥)。

下图中的两条割边用虚线标识:
在这里插入图片描述
不难发现,桥一定是搜索树种的边,一个简单环中的边一定都不是桥

需要注意的是,在求一张无向图中所有的割边时,因为深度优先遍历的是无向图,所以从每个节点 u u u出发,总能访问到它的父结点 f a fa fa。根据追溯值 l o w low low的计算方法, ( u , f a ) (u,fa) (u,fa)属于搜索树上的边,且 f a fa fa不是 u u u的子节点,所以不能用 f a fa fa的时间戳来更新 l o w [ u ] low[u] low[u]

但是如果只记录每个节点的父节点,会无法处理重边的情况——当 u u u f a fa fa之间有多条边时, ( u , f a ) (u,fa) (u,fa)一定不是桥。在这些重边中,只有一条算是搜索树上的边,其它重边都不算。故有重边时, d f n [ f a ] dfn[fa] dfn[fa]能用来更新 l o w [ u ] low[u] low[u]

一个好的解决方案是:将记录 f a fa fa改为记录递归进入每个节点的边的编号 f r o m from from。若沿着编号为 f r o m from from的边递归进入了节点 u u u,则忽略从 u u u出发相对于 f r o m from from的反向边。

算法实现

  • 基于上述分析,可以通过Tarjan求出图中所有的桥边,同时也能求出图中的边双连通分量。
  • 如果将每个边双连通分量缩成一个点,原图就变成一棵树,桥就是树的边。
    在这里插入图片描述
  • 然后只需将在树中所有叶子节点间添加边,将树变成一个边双连通分量,就能保证整个图中任意两点之间都至少有两条“相互分离的路径”。如下图所示:
    在这里插入图片描述

由于叶子节点的度都为 1 1 1,因此只需要统计处度为 1 1 1的节点个数 c n t cnt cnt,最终答案为 ⌈ c n t 2 ⌉ \lceil \frac {cnt}{2}\rceil 2cnt

代码实现

#include <bits/stdc++.h>
using namespace std;
const int N = 5005, M = 40005;
int h[N], e[M], ne[M], idx;
int n, m;
int dfn[N], low[N], timestamp, stk[N], top, dcc_cnt, id[N], d[M];
bool bridge[M]; //标记是否为桥(割边)
void add(int a, int b)  // 添加一条边a->b
{e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
//from表示进入节点u的边
void tarjan(int u, int from) 
{dfn[u] = low[u] = ++ timestamp;stk[++ top] = u;for(int i = h[u]; ~ i; i = ne[i]){int v = e[i];if(!dfn[v]) //v点没有访问,则边i是搜索树上的边{tarjan(v, i);low[u] = min(low[u], low[v]);if(dfn[u] < low[v]) //v无法回到u,说明当前边是桥bridge[i] = bridge[i ^ 1] = true; //将正向边、反向边标记为桥}else //边i不是搜索树上的边{ if(i != (from ^ 1)) //边i不是from的反向边low[u] = min(low[u], dfn[v]); }}if(dfn[u] == low[u]) //u是双连通分量的最高点{//取出该双连通分量中的所有点进行标记++ dcc_cnt;int v;do {v = stk[top --];id[v] = dcc_cnt;} while(u != v);}
}
int main()
{cin >> n >> m;memset(h, -1, sizeof h);while(m --){int a, b;cin >> a >> b;add(a, b), add(b, a);}tarjan(1, -1); //每对草场之间已经有至少一条路径,是连通的,因此从顶点1出发即可//遍历每条边for(int i =  0; i < idx; i ++){if(bridge[i]) //如果是桥d[id[e[i]]] ++; //给桥的两个顶点所在的双连通分量的度数增加1}int cnt = 0; //统计叶子节点的个数,即度为1的节点个数for(int i = 1; i <= dcc_cnt; i ++){if(d[i] == 1) cnt ++;}cout << (cnt + 1) / 2 << endl;
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

对BSV区块链的曼达拉网络通俗易懂的解释

​​发表时间&#xff1a;2023年6月15日 BSV区块链正在引入“曼达拉”升级&#xff0c;使BSV区块链网络的拓扑结构能够适配Teranode&#xff0c;适配这个可以大幅扩容的节点软件。BSV区块链上曼达拉网络的概念并不会改变整个系统的核心规则&#xff1b;相反&#xff0c;它能够引…

I2C接口+高度集成的电源管理芯片(PMIC)-iML1942

电源管理芯片 - iML1942是一个高度集成的电源管理IC为TFT液晶面板。它具有完整的I2C接口来编程各种参数。该设备包括一个针对AVDD的电流模式升压调节器&#xff0c;一个针对VBK1的同步升压转换器。VGL可选的反相转换器或负电荷泵调节器&#xff0c;VSS1负线性调节器&#xff0c…

细说MCU的ADC模块单通道连续采样的实现方法

目录 一、工程依赖的硬件及背景 二、设计目的 三、建立工程 1、配置GPIO 2、选择时钟源和Debug 3、配置ADC 4、配置系统时钟和ADC时钟 5、配置TIM3 6、配置串口 四、代码修改 1、重定义TIM3中断回调函数 2、启动ADC及重写其回调函数 3、定义用于存储转换结果的数…

Redis---9---集群(cluster)

将新增的6387节点&#xff08;空槽号&#xff09;作为master节点加入原集群 Redis—9—集群&#xff08;cluster&#xff09; 是什么 定义 ​ 由于数据量过大&#xff0c;单个Master复制集难以承担&#xff0c;因此需要对多个复制集进行集群&#xff0c;形成水平扩展每个复…

苹果电脑能玩赛博朋克2077吗 如何在mac上运行赛博朋克2077 crossover能玩什么游戏

各位喜欢赛博朋克风的一定不能错过《赛博朋克2077》。那么《赛博朋克2077》是一款什么样的游戏&#xff1f;《赛博朋克2077》在苹果电脑上可以运行吗&#xff1f;一起来看看介绍吧。 一、《赛博朋克2077》是一款什么样的游戏&#xff1f; 《赛博朋克2077》是一款由CD Projekt …

重温react-13(嵌套路由和重定向等)

重定向和404 import React from react; import { Routes, Route, Link,NavLink ,Navigate} from react-router-dom; import Home from ./Home/Home import About from ./About/About import News from ./News/News import NotFound from ./NotFound/NotFound; export default …

pytest-yaml-sanmu(六):YAML数据驱动测试

如果说 pytest 中哪些标记使用得最多&#xff0c;那无疑是 parametrize 了&#xff0c; 它为用例实现了参数化测试的能力&#xff0c;进而实现了数据驱动测试的能力。 1. 使用标记 parametrize 的使用需要提高两个内容&#xff1a; 参数名 参数值 pytest 在执行用例时&…

eBPF 指令宏

linux 6.9.7 指令宏 /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ /* eBPF instruction mini library */ #ifndef __BPF_INSN_H #define __BPF_INSN_Hstruct bpf_insn;/* ALU ops on registers, bpf_add|sub|...: dst_reg src_reg */ // BPF_ALU64_REG&am…

Drools开源业务规则引擎(三)- 事件模型(Event Model)

文章目录 Drools开源业务规则引擎&#xff08;三&#xff09;- 事件模型&#xff08;Event Model&#xff09;1.org.kie.api.event2.RuleRuntimeEventManager3.RuleRuntimeEventListener接口说明示例规则文件规则执行日志输出 4.AgentaEventListener接口说明示例监听器实现类My…

Linux——学习Linux基本工具安装教程视频链接

本篇文章就是记录一下学习Linux需要用到的基本工具的视频教程链接&#xff0c;方便以后查看 VMware15.5安装 安装视频教程&#xff1a;VMware15.5安装教程 centos7.6安装&#xff08;这个视频教程真的很nice&#xff09; 视频教程&#xff1a;centos7.6 虚拟机克隆、快照、…

医疗器械FDA | FDA如何对医疗器械网络安全认证进行审查?

FDA医械网络安全文件出具​https://link.zhihu.com/?targethttps%3A//www.wanyun.cn/Support%3Fshare%3D24315_ea8a0e47-b38d-4cd6-8ed1-9e7711a8ad5e FDA对医疗器械的网络安全认证进行审查时&#xff0c;主要关注以下几个方面&#xff0c;以确保医疗器械在网络环境中的安全性…

7 系列 FPGA 引脚及封装(参考ug475)

目录 I/O BankPins引脚定义I/O and Multi-Function PinsPower Supply PinsDedicated XADC PinsTransceiver PinsDedicated Configuration PinsTemperature Sensor Pins Device 视图整个 FPGAIOBILOGIC,OLOGIC,IDELAY,ODELAYBUFIO,BUFR,IDELAYCTRLBUFMRCEBRAM,DSPIBUFDS_GTE2CLB…

vscode远程连接linux(配置免密)

远程连接 1.首先保证物理机和虚拟机网络可以ping通 2.查看ubuntu得ip地址 ifconfig IP为&#xff1a;192.168.52.133 3.连接远程主机 配置免密 1.打开cmd运行ssh-keygen -t rsa 一路回车就行 2.打开window文件夹C:\Users\xbj\.ssh 3.用记事本打开id_rsa.pub文件复制公…

[数据集][目标检测]护目镜检测数据集VOC+YOLO格式888张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;888 标注数量(xml文件个数)&#xff1a;888 标注数量(txt文件个数)&#xff1a;888 标注类别…

美光科技在2024年1γ工艺技术在10纳米级别启动EUV试产

美光科技&#xff08;Micron&#xff09;在2024年针对其1γ&#xff08;1-gamma&#xff09;工艺技术在10纳米级别启动EUV&#xff08;极紫外光刻&#xff09;试产&#xff0c;这标志着存储行业巨头在EUV采用上的重要一步&#xff0c;尽管相比英特尔和台积电等其他半导体制造商…

Linux--USB驱动开发(一)USB简介

一、什么是 USB&#xff1f; USB 全称为 Universal Serial Bus &#xff0c;翻译过来就是通用串行总线。由英特尔与众多电脑公司提出来&#xff0c;用于规范电脑与外部设备的连接与通讯。目前 USB 接口已经得到了大范围的应用&#xff0c;已经是电脑、手机等终端设备的必配…

Android面试题自定义View之Window、ViewRootImpl和View的三大流程

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 View的三大流程指的是measure(测量)、layout(布局)、draw(绘制)。 下面我们来分别看看这三大流程 View的measure(测量) MeasureSpec Measur…

【Axure高保真原型】中继器表格——移入显示详情卡片案例

今天和大家分享中继器表格——移入显示详情卡片的原型模板&#xff0c;鼠标移入员工号或姓名会弹出员工卡片&#xff0c;可以查看更详细的信息。这个表格是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;只需要维护中继器表格里的信息&#xff0c;即可自动生成交互效…

计算机网络--网络层

一、网络层的服务和功能 网络层主要为应用层提供端对端的数据传输服务 网络层接受运输层的报文段&#xff0c;添加自己的首部&#xff0c;形成网络层分组。分组是网络层的传输单元。网络层分组在各个站点的网络层之间传输&#xff0c;最终到达接收方的网络层。接收方网络层将运…

基于单片机的防酒驾控制系统设计

摘 要&#xff1a; 酒后驾车的危害十分巨大&#xff0c;因此&#xff0c;笔者介绍了一种基于单片机的防酒驾控制系统。系统由酒精传感器 MQ-3测量汽车驾驶员体内的酒精含量浓度&#xff0c;通过 A/D 转换器转换成数字信号传给单片机&#xff0c;经过单片机处理后显示酒精浓度&a…