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

题解:AcWing 395 冗余路径

本文分享的必刷题目是从蓝桥云课洛谷AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。

欢迎大家订阅我的专栏:算法题解:C++与Python实现!

附上汇总贴:算法竞赛备考冲刺必刷题(C++) | 汇总


【题目来源】

AcWing:395. 冗余路径 - AcWing题库

【题目描述】

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

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

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

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

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

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

可能有不止一条道路直接连接同一对草场,尽管如此,你仍可以在它们之间再建一条道路,作为另一条不同的道路。

【输入】

1 11行输入F FFR RR

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

【输出】

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

【输入样例】

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

【输出样例】

2

【核心思想】

  1. 问题分析:给定无向连通图,要求添加最少的新边,使得任意两点之间至少有两条边不相交的路径。这等价于使图变为边双连通图(不存在桥)。关键在于理解:桥是限制边双连通性的唯一障碍,只要消除所有桥,图就变为边双连通。

  2. 算法选择

    • Tarjan算法求桥:使用l o w lowlow数组和d f n dfndfn数组找出所有桥边
    • 边双连通分量分解:将图分解为若干个边双连通分量(去掉桥后的连通块)
    • 缩点建树:将每个边双连通分量缩成一个点,桥作为边,构建一棵树
    • 叶子节点配对:统计缩点树中的叶子节点数,答案为( l e a f + 1 ) / 2 (leaf + 1) / 2(leaf+1)/2
  3. 关键步骤

    • Step 1 - Tarjan求桥

      • DFS遍历图,维护d f n [ x ] dfn[x]dfn[x](时间戳)和l o w [ x ] low[x]low[x](能通过回边到达的最小时间戳)
      • 对于边( x , y ) (x, y)(x,y),若l o w [ y ] > d f n [ x ] low[y] > dfn[x]low[y]>dfn[x],则该边是桥
      • 使用异或技巧i ^ 1处理双向边,避免重复访问
    • Step 2 - 边双连通分量分解

      • 使用栈记录当前DFS路径上的节点
      • d f n [ x ] = l o w [ x ] dfn[x] = low[x]dfn[x]=low[x]时,找到边双连通分量的根
      • 弹出栈中节点直到x xx,这些节点构成一个边双连通分量
    • Step 3 - 缩点建树

      • 每个边双连通分量缩成一个点
      • 桥边连接不同的分量,形成一棵树
    • Step 4 - 统计叶子节点

      • 遍历所有桥边,统计每个分量在缩点树中的度数
      • 度数为1的分量是叶子节点
    • Step 5 - 计算答案

      • 设叶子节点数为c n t cntcnt
      • 答案为( c n t + 1 ) / 2 (cnt + 1) / 2(cnt+1)/2,即把叶子两两配对需要的边数
  4. 时间/空间复杂度

    • 时间复杂度:O ( F + R ) O(F + R)O(F+R),Tarjan算法和后续处理均为线性时间
    • 空间复杂度:O ( F + R ) O(F + R)O(F+R),存储图和各种辅助数组
  5. 边双连通分量与桥的核心思想

    • 桥的定义:删除后会使图不连通的边,是边双连通性的瓶颈
    • 边双连通分量:不含桥的最大连通子图,分量内任意两点有两条边不相交路径
    • 缩点树的性质:将边双连通分量缩点后,桥形成一棵树,叶子节点代表"边缘"分量
    • 最优加边策略:在缩点树中,连接两个叶子节点可以同时减少两个叶子的度数
    • 答案公式( l e a f + 1 ) / 2 (leaf + 1) / 2(leaf+1)/2表示将l e a f leafleaf个叶子两两配对(向上取整)
    • 适用于网络可靠性分析、关键路径识别、图加固优化类问题

【算法标签】

#双连通分量

【代码详解】

#include<bits/stdc++.h>usingnamespacestd;// ================= 常量与全局变量 =================constintN=5000;// 最大节点数constintM=20005;// 最大边数(注意双向边,所以是两倍)intn,m;// n: 节点数, m: 边数// ---------- 邻接表相关 ----------inth[N];// 邻接表头结点inte[M];// 存储边的终点intne[M];// 存储下一条边的索引intidx;// 边的编号计数器(从0开始)// ---------- Tarjan 相关 ----------intdfn[N];// 时间戳:节点首次被访问的顺序intlow[N];// 能通过回边到达的最小时间戳inttot;// 时间戳计数器stack<int>stk;// 栈,用于存储当前正在处理的节点// ---------- 桥与边双连通分量相关 ----------intbri[M];// 标记边是否为桥(1表示是桥)intcon_cnt;// 边双连通分量的总数intcon_id[N];// 每个节点所属的边双连通分量编号intcon_size[N];// 每个边双连通分量的大小intd[N];// 每个边双连通分量的度数(在缩点树中的度数)vector<int>ans[N];// 存储每个边双连通分量包含的节点// ================= 添加边 =================voidadd(inta,intb){e[idx]=b;// 存储边的终点ne[idx]=h[a];// 头插法h[a]=idx++;// 更新头结点}// ================= Tarjan 算法求边双连通分量 =================voidtarjan(intx,intin_edge){dfn[x]=low[x]=++tot;// 初始化时间戳stk.push(x);// 当前节点入栈// 遍历 x 的所有邻接点for(inti=h[x];i!=-1;i=ne[i]){inty=e[i];// 邻接点 yif(!dfn[y])// y 尚未访问{tarjan(y,i);// 递归访问 ylow[x]=min(low[x],low[y]);// 用子树更新 low[x]// 判断边 (x, y) 是否为桥if(low[y]>dfn[x]){// 标记该边和其反向边为桥bri[i]=bri[i^1]=true;}}// 如果是回退边,且不是刚刚过来的那条反向边elseif(i!=(in_edge^1)){low[x]=min(low[x],dfn[y]);// 用回边更新 low[x]}}// 如果 x 是边双连通分量的根节点if(dfn[x]==low[x]){++con_cnt;// 新建一个边双连通分量while(1){inty=stk.top();// 取出栈顶节点stk.pop();// 弹出栈顶con_id[y]=con_cnt;// 记录节点 y 所属的分量编号con_size[con_cnt]++;// 更新分量大小ans[con_cnt].push_back(y);// 将节点加入该分量if(y==x)// 直到弹出 x 为止break;}}}// ================= 主函数 =================intmain(){// 读取节点数和边数cin>>n>>m;// 初始化邻接表头结点为 -1memset(h,-1,sizeof(h));// 读入 m 条无向边while(m--){intu,v;cin>>u>>v;add(u,v);// 添加正向边add(v,u);// 添加反向边}// 从节点 1 开始执行 Tarjan 算法tarjan(1,0);// ========= 统计缩点后每个分量的度数 =========for(inti=0;i<idx;i++){if(bri[i])// 如果该边是桥{d[con_id[e[i]]]++;// 该边终点所在分量的度数加1}}// ========= 统计叶子节点(度数为1的分量) =========intcnt=0;for(inti=1;i<=con_cnt;i++){if(d[i]==1)// 度数为1,即为叶子节点{cnt++;}}// ========= 输出答案 =========// 将叶子节点两两配对,需要的最少边数为 (cnt + 1) / 2cout<<(cnt+1)/2<<endl;return0;}

【运行结果】

7 7 1 2 2 3 3 4 2 5 4 5 5 6 5 7 2
http://www.jsqmd.com/news/1034567/

相关文章:

  • 11603华夏之光永存:黄大年茶思屋榜文116期 第3题C+L波段可调高功率窄线宽片上光源硬核工程解题报告
  • PC微信3.9.2.23消息结构体逆向分析:从内存布局到收发标记揭秘
  • 移动端自动化数据采集实战:Appium与mitmproxy双轨方案解析
  • 【毕业设计】基于 Spring Boot 的政务事项申报审批管理系统的设计与实现 基于 Spring Boot 的基层电子政务运维管理平台(源码+文档+远程调试,全bao定制等)
  • Material Sense 性能优化:3个技巧提升React Material UI应用加载速度
  • RPA与pytest-metadata集成:构建可观测的自动化测试框架
  • 登报遗失声明一般多少钱?登报遗失声明如何办理呢?
  • 如何在iPhone/iPad上完整运行Minecraft Java版?PojavLauncher终极指南
  • 手把手教你用Docker容器部署DNF私服:从零到开服的完整指南
  • 终极Windows Defender修复指南:no-defender工具的决策流程图解法
  • 揭秘无锡永辉大推拉雨棚,遮阳效果与满意度 - myqiye
  • Bedrock Guardrails 新 API:不用创建资源,直接给 Agent 每一步加安全检查
  • Apple Silicon双系统实战指南:深度解析Asahi Linux部署与安全配置
  • Windows 7系统激活全解析:从授权原理到合规操作指南
  • Windows界面定制深度解析:ExplorerPatcher技术实现与专业级应用指南
  • ExplorerPatcher完全卸载指南:3种核心方案解决Windows系统深度集成难题
  • 深度剖析:IQKeyboardManager的架构设计与实现机制
  • 3步搞定华硕笔记本风扇异常:G-Helper智能散热控制指南
  • AI代理自发卡特尔现象:隐式协调与目标漂移的工程实证
  • 3个实战场景:用yfinance解决金融数据处理中的真实痛点
  • CAST模型:程序化视频检索的技术突破与应用
  • 电脑监控软件哪个好用?精选6款企业级电脑监控软件,最新排行榜
  • Compound Engineering:革命性AI驱动开发工作流引擎
  • 无源电磁场传感器:磁热效应液晶技术解析与应用
  • 不锈钢水箱哪家好?金泽供水实力剖析 - myqiye
  • Windows系统彻底退出微软账户的四种方法:从常规设置到命令行强制解除
  • 2026年英文论文降AIGC指南:从94%压到7%的实操方法与4款工具盘点 - 降AI实验室
  • Spicetify配置文件详解:Spicetify.ini参数设置与优化技巧
  • 小米手表表盘设计终极指南:5分钟掌握Mi-Create免费可视化工具
  • Bedrock Guardrails 新 API 实战:无需创建资源,给 Agent 每一步加安全检查