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

题解:AcWing 396 矿场搭建

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

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

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


【题目来源】

AcWing:396. 矿场搭建 - AcWing题库

【题目描述】

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。

为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。

于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。

请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

【输入】

输入文件有若干组数据,每组数据的第一行是一个正整数N NN,表示工地的隧道数。

接下来的N NN行每行是用空格隔开的两个整数S SST TTS ≠ T S≠TS=T),表示挖煤点S SS与挖煤点T TT由隧道直接连接。

注意,每组数据的挖煤点的编号为1 ∼ M a x 1∼Max1Max,其中M a x MaxMax表示由隧道连接的挖煤点中,编号最大的挖煤点的编号,可能存在没有被隧道连接的挖煤点。

输入数据以0 00结尾。

【输出】

每组数据输出结果占一行。

其中第i ii行以Case i:开始(注意大小写,Casei之间有空格,i:之间无空格,:之后有空格)。

其后是用空格隔开的两个正整数,第一个正整数表示对于第i ii组输入数据至少需要设置几个救援出口,第二个正整数表示对于第i ii组输入数据不同最少救援出口的设置方案总数。

输入数据保证答案小于2 64 2^{64}264,输出格式参照以下输入输出样例。

【输入样例】

9 1 3 4 1 3 5 1 2 2 6 1 5 6 3 1 6 3 2 6 1 2 1 3 2 4 2 5 3 6 3 7 0

【输出样例】

Case 1: 2 4 Case 2: 4 1

【核心思想】

  1. 问题分析:给定一个无向图,需要在某些节点设置救援出口,使得删除任意一个节点(挖煤点坍塌)后,剩余节点都能到达某个救援出口。这等价于求点双连通分量中的割点分布,并在每个点双连通分量中合理设置出口。

  2. 算法选择

    • Tarjan 算法求点双连通分量:通过d f n dfndfnl o w lowlow数组找出图中的所有点双连通分量
    • 割点判定:利用l o w [ y ] ≥ d f n [ x ] low[y] \geq dfn[x]low[y]dfn[x]判断节点x xx是否为割点
    • 分类讨论:根据每个点双连通分量中割点的数量,决定需要设置的出口数量和方案数
  3. 关键步骤

    • 建图:读入m mm条边,构建无向图邻接表,确定最大节点编号n nn
    • Tarjan 求点双连通分量
      • 对每个未访问的节点作为根节点执行t a r j a n ( r o o t ) tarjan(root)tarjan(root)
      • 使用栈维护当前 DFS 路径上的节点
      • 当发现l o w [ y ] ≥ d f n [ x ] low[y] \geq dfn[x]low[y]dfn[x]时,找到一个点双连通分量,将栈中节点弹出直到y yy
    • 统计每个分量中的割点数量t m p tmptmp
      • 情况1t m p = 0 tmp = 0tmp=0,无割点):
        • 若分量大小为1 11(孤立点):需要设置1 11个出口,方案数× 1 \times 1×1
        • 若分量大小≥ 2 \geq 22:需要设置2 22个出口,方案数× C ( s i z e , 2 ) = s i z e × ( s i z e − 1 ) 2 \times C(size, 2) = \frac{size \times (size-1)}{2}×C(size,2)=2size×(size1)
      • 情况2t m p = 1 tmp = 1tmp=1,一个割点):需要设置1 11个出口在非割点位置,方案数× ( s i z e − 1 ) \times (size - 1)×(size1)
      • 情况3t m p ≥ 2 tmp \geq 2tmp2,多个割点):该分量被多个割点保护,无需设置出口
    • 输出答案:最少出口数r e s resres和总方案数n u m numnum
  4. 时间/空间复杂度

    • 时间复杂度:O ( n + m ) O(n + m)O(n+m),Tarjan 算法线性遍历所有节点和边
    • 空间复杂度:O ( n + m ) O(n + m)O(n+m),存储图结构和辅助数组
  5. 点双连通分量与割点的核心思想

    • 点双连通分量:图中删除任意一个节点后仍然连通的极大子图
    • 割点作用:割点是连接不同点双连通分量的"桥梁",删除割点会将图分割成多个连通块
    • 出口设置策略
      • 无割点的分量:必须内部设置出口,且至少2 22个(防止出口所在点坍塌)
      • 有一个割点的分量:割点坍塌时分量孤立,需在非割点设1 11个出口
      • 有多个割点的分量:任意点坍塌都能通过其他割点逃出,无需设出口
    • 孤立点特判:单个孤立点只需设置1 11个出口(自己就是出口)
    • 适用于网络可靠性、关键节点识别、冗余设计类问题

【算法标签】

#双连通分量

【代码详解】

#include<bits/stdc++.h>usingnamespacestd;// 定义无符号长整型别名,用于存储方案数typedefunsignedlonglongULL;// ================= 常量与全局变量 =================constintN=1005;// 最大节点数intn;// 当前测试用例的节点数intm;// 当前测试用例的边数vector<int>e[N];// 原图邻接表vector<int>ne[N];// 未使用的邻接表(保留原样)// ---------- Tarjan 相关 ----------intdfn[N];// 时间戳:节点首次被访问的顺序intlow[N];// 能通过回边到达的最小时间戳inttot;// 时间戳计数器intcnt;// 点双连通分量计数器stack<int>stk;// 栈,存储当前正在处理的节点vector<int>dcc[N];// 存储每个点双连通分量包含的节点// ---------- 割点相关 ----------intcut[N];// 标记节点是否为割点(1表示是割点)introot;// 当前 DFS 树的根节点intnum;// 方案数(用于统计)intid[N];// 未使用的数组(保留原样)// ================= Tarjan 算法求点双连通分量 =================voidtarjan(intx){dfn[x]=low[x]=++tot;// 初始化时间戳stk.push(x);// 当前节点入栈// 孤立点:单独形成一个点双连通分量if(!e[x].size()){dcc[++cnt].push_back(x);return;}intchild=0;// 记录当前节点在 DFS 树中的子节点数// 遍历 x 的所有邻接点for(inty:e[x]){if(!dfn[y])// y 尚未访问{tarjan(y);// 递归访问 ylow[x]=min(low[x],low[y]);// 用子树更新 low[x]// 判断 x 是否为割点if(low[y]>=dfn[x]){child++;// 如果 x 不是根节点,或者 x 是根节点且有多个子节点if(x!=root||child>1)cut[x]=true;// 找到一个点双连通分量cnt++;while(1){intz=stk.top();// 取出栈顶stk.pop();// 弹出栈顶dcc[cnt].push_back(z);// 将节点加入当前分量if(z==y)// 直到弹出 y 为止break;}dcc[cnt].push_back(x);// 将 x 也加入当前分量}}else// 发现回边{low[x]=min(low[x],dfn[y]);// 用回边更新 low[x]}}}// ================= 主函数 =================intmain(){intT=1;// 测试用例编号// 多组测试数据,直到 m = 0 结束while(cin>>m,m){// 清空上一组数据for(inti=1;i<=cnt;i++)dcc[i].clear();for(inti=1;i<=n;i++)e[i].clear();while(!stk.empty())stk.pop();// 重置计数器tot=cnt=n=0;memset(cut,0,sizeof(cut));memset(dfn,0,sizeof(dfn));// 读入 m 条边while(m--){inta,b;cin>>a>>b;n=max(n,a);// 更新最大节点编号n=max(n,b);e[a].push_back(b);// 添加无向边e[b].push_back(a);}// 对每个未访问的节点执行 Tarjan 算法for(root=1;root<=n;root++)if(!dfn[root])tarjan(root);// ========= 统计答案 =========intres=0;// 最少需要放置的消防站数量ULL num=1;// 方案数// 遍历每个点双连通分量for(inti=1;i<=cnt;i++){inttmp=0;// 当前分量中割点的数量// 统计当前分量中割点的个数for(intj=0;j<dcc[i].size();j++)if(cut[dcc[i][j]])tmp++;// 情况1:分量中没有割点if(tmp==0){// 孤立点:需要放 1 个消防站if(dcc[i].size()==1)res+=1;// 非孤立点:需要放 2 个消防站,方案数为 C(size, 2)else{res+=2;num*=(ULL)dcc[i].size()*(dcc[i].size()-1)/2;}}// 情况2:分量中有 1 个割点elseif(tmp==1){res++;// 需要在非割点中放 1 个消防站num*=dcc[i].size()-1;// 方案数为非割点个数}// 情况3:分量中有 2 个及以上割点,无需放置消防站}// 输出结果printf("Case %d: %d %llu\n",T++,res,num);}return0;}

【运行结果】

9 1 3 4 1 3 5 1 2 2 6 1 5 6 3 1 6 3 2 Case 1: 2 4 6 1 2 1 3 2 4 2 5 3 6 3 7 Case 2: 4 1 0
http://www.jsqmd.com/news/1060519/

相关文章:

  • 崩坏星穹铁道自动化终极方案:三月七小助手让你每天多玩2小时
  • 商圈实测成都锦江区黄金回收价格与避坑指南 - 上门黄金回收
  • 红色生态游平台:融合红色文化与绿色发展的时代背景与战略意义
  • 大语言模型性能受提示词礼貌策略影响:多语言场景下的工程优化实践
  • KrkrzExtract:5分钟上手,让视觉小说资源处理变得简单高效
  • Android Smart Lock 集成深度解析:系统级凭据管理原理与落地实践
  • 参与式设计:AI伦理社区治理的实践方法与FAccT案例剖析
  • 2026成都黄金回收实战经验!最新门店排行新鲜出炉 - 奢品小当家
  • 2026杭州装修公司深度剖析:基于多维度数据评选的六家优质榜单 - 资讯报道
  • Mermaid Live Editor:3分钟从代码新手到图表专家的神奇之旅
  • 微信投票制作步骤分享,一分钟学会小白也能搞定! - 微信投票小程序
  • DeepSeek GPU算子深度解析:RoPE、MLA、DSA与FlashAttention-2硬件实现
  • 宝鸡黄金变现看这篇!六家靠谱店铺覆盖全市区县,卖金子不走弯路 - 清奢黄金上门回收
  • 深度解析:APK图标编辑器技术架构与实现原理
  • 英伟达等联合推出ENPIRE框架:AI自动做机器人研究,四任务成功率达99%!
  • 嵌入式调试利器:NT-Shell在LPC5500上的移植与应用实战
  • 美国FBA空派物流哪些好? - 恒盛通物流
  • AI电竞教练如何实现毫秒级操作分析与意图建模
  • Arch Linux原生部署ownCloud:LAMP栈深度配置与生产级调优
  • Windows APK安装器:告别安卓模拟器,三步在电脑上运行手机应用
  • 怎么判断自己适不适合搞算法/科研?先来闯这“5关”试试(3SAT篇)
  • 五分钟实现Windows文件管理器视觉革命:从单调界面到半透明艺术
  • FineCog-Nav:基于细粒度认知与大模型的无人机零样本视觉语言导航
  • SSH隧道原理解析:本地/远程/动态端口转发实战
  • 硬件级AI治理:芯片计量与供应链控制技术解析
  • 2026年6月最新|涂胶机生产厂家实力排名 实地测评权威榜单出炉 - 商业新知
  • MPC5200 BestComm DMA引擎调试与性能优化实战指南
  • NAATI 翻译驾照是什么?NAATI 翻译驾照怎么办?别等被罚才后悔! - 慧办好
  • 登报声明去哪里登报?登报声明多少钱? - 慧办好
  • 多智能体强化学习中的合作脆弱性与RATTL算法解析