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

UVa 10989 Bomb Divide and Conquer

题目描述

敌人有 nnn 个城市,通过 mmm 条道路连接。我们拥有可以摧毁道路的轰炸机。"轰炸、分治"策略指出,如果我们将敌人的城市彼此分离,那么两个(或更多)部分将更容易控制。轰炸一条道路有相应的成本(燃料、风险因素等)。确保存在至少一对城市之间没有路径所需的最小总轰炸成本是多少?

输入格式

  • 第一行给出测试用例的数量 NNN
  • 每个测试用例包含:
    • 两个整数 nnn (2≤n≤1502 \leq n \leq 1502n150) 和 mmm (0≤m≤n(n−1)/20 \leq m \leq n(n-1)/20mn(n1)/2)
    • mmm 行,每行包含三个整数:两个不同的城市编号(111nnn)和摧毁该道路的成本(111100010001000

输出格式

  • 对于每个测试用例,输出一行:Case #x: 后跟断开某对城市连接的总成本

题目分析

问题本质

题目要求通过摧毁一些道路,使得图变得不连通,即至少存在一对城市之间没有路径。这等价于将原图分成至少两个连通分量。

我们需要找到摧毁道路的最小总成本,使得图不再连通。

关键洞察

  1. 图连通性:如果原图已经是不连通的,那么不需要摧毁任何道路,答案为 000

  2. 最小割问题:对于连通图,我们需要找到最小割——将顶点分成两个集合 SSSTTT,使得连接 SSSTTT 的所有边的权重之和最小

  3. 最大流最小割定理:在流网络中,从源点 sss 到汇点 ttt 的最大流值等于分离 sssttt 的最小割的容量

算法选择

由于 n≤150n \leq 150n150,我们可以使用以下方法:

  • 固定一个源点 sss
  • 枚举所有其他顶点作为汇点 ttt
  • 对于每个 (s,t)(s, t)(s,t) 对,计算最大流(即最小割)
  • 取所有 (s,t)(s, t)(s,t) 对的最小割的最小值作为答案

我们选择 Dinic\texttt{Dinic}Dinic 算法 来计算最大流,因为它在实践中效率较高。

复杂度分析

  • Dinic\texttt{Dinic}Dinic 算法复杂度:O(V2E)O(V^2E)O(V2E)
  • 我们需要计算 n−1n-1n1 次最大流
  • 总复杂度:O(n⋅V2E)O(n \cdot V^2E)O(nV2E),在 n≤150n \leq 150n150 时可行

解题思路详解

步骤 1:问题建模

将问题转化为图论模型:

  • 顶点:城市(编号 000n−1n-1n1
  • 边:道路,边权 = 摧毁成本
  • 目标:找到全局最小割

步骤 2:算法原理

根据全局最小割定理,无向图的全局最小割可以通过以下方式求得:

  1. 固定一个源点 sss(通常选择顶点 000
  2. 对于每个其他顶点 ttt,计算 sss-ttt 最小割
  3. 全局最小割 = min⁡t≠smincut(s,t)\min\limits_{t \neq s} \text{mincut}(s, t)t=sminmincut(s,t)

步骤 3:Dinic\texttt{Dinic}Dinic 算法实现

Dinic\texttt{Dinic}Dinic 算法包含三个主要部分:

  1. 分层图构建BFS\texttt{BFS}BFS):

    • 从源点开始,为每个顶点分配层级
    • 只允许从低层级向高层级发送流量
  2. 阻塞流计算DFS\texttt{DFS}DFS):

    • 在当前分层图中寻找增广路径
    • 使用当前弧优化避免重复检查边
  3. 迭代过程

    • 重复构建分层图和计算阻塞流,直到无法找到增广路径

步骤 4:特殊情况处理

  • 如果原图不连通,最小割为 000
  • 对于单边情况,最小割可能是该边的权重
  • 对于复杂网络,需要计算多个 (s,t)(s, t)(s,t) 对的最小割

代码实现

// Bomb Divide and Conquer
// UVa ID: 10989
// Verdict: Accepted
// Submission Date: 2025-11-02
// UVa Run Time: 0.160s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>using namespace std;/*** Dinic 最大流算法类* 用于计算无向图的最小割*/
struct DinicMaxFlow {// 边结构体struct FlowEdge {int targetNode;          // 目标节点int reverseEdgeIndex;    // 反向边在邻接表中的索引int capacity;            // 边容量int currentFlow;         // 当前流量};int totalNodes;                      // 总节点数vector<vector<FlowEdge>> graph;      // 图的邻接表表示vector<int> nodeLevel;               // 节点的层级(用于分层图)vector<int> currentEdgePointer;      // 当前弧指针(用于优化)// 构造函数DinicMaxFlow(int nodeCount) : totalNodes(nodeCount), graph(nodeCount), nodeLevel(nodeCount), currentEdgePointer(nodeCount) {}/*** 添加无向边* @param from 起始节点* @param to 目标节点  * @param cap 边的容量*/void addUndirectedEdge(int from, int to, int cap) {// 添加正向边graph[from].push_back({to, (int)graph[to].size(), cap, 0});// 添加反向边(无向图,容量相同)graph[to].push_back({from, (int)graph[from].size() - 1, cap, 0});}/*** 使用 BFS 构建分层图* @param source 源点* @param sink 汇点* @return 如果汇点可达返回 true,否则 false*/bool buildLevelGraph(int source, int sink) {fill(nodeLevel.begin(), nodeLevel.end(), -1);nodeLevel[source] = 0;queue<int> bfsQueue;bfsQueue.push(source);while (!bfsQueue.empty()) {int currentNode = bfsQueue.front();bfsQueue.pop();for (const FlowEdge& edge : graph[currentNode]) {if (nodeLevel[edge.targetNode] == -1 && edge.currentFlow < edge.capacity) {nodeLevel[edge.targetNode] = nodeLevel[currentNode] + 1;bfsQueue.push(edge.targetNode);}}}return nodeLevel[sink] != -1;}/*** 使用 DFS 在分层图中寻找阻塞流* @param currentNode 当前节点* @param sink 汇点* @param minCapacity 路径上的最小容量* @return 实际推送的流量*/int findBlockingFlow(int currentNode, int sink, int minCapacity) {if (currentNode == sink) return minCapacity;for (int& edgeIndex = currentEdgePointer[currentNode]; edgeIndex < (int)graph[currentNode].size(); edgeIndex++) {FlowEdge& edge = graph[currentNode][edgeIndex];if (nodeLevel[edge.targetNode] == nodeLevel[currentNode] + 1 && edge.currentFlow < edge.capacity) {int pushedFlow = findBlockingFlow(edge.targetNode, sink, min(minCapacity, edge.capacity - edge.currentFlow));if (pushedFlow > 0) {edge.currentFlow += pushedFlow;graph[edge.targetNode][edge.reverseEdgeIndex].currentFlow -= pushedFlow;return pushedFlow;}}}return 0;}/*** 计算从 source 到 sink 的最大流* @param source 源点* @param sink 汇点* @return 最大流值(即最小割值)*/int computeMaxFlow(int source, int sink) {int totalFlow = 0;// 反复构建分层图并寻找阻塞流while (buildLevelGraph(source, sink)) {fill(currentEdgePointer.begin(), currentEdgePointer.end(), 0);while (int pushedFlow = findBlockingFlow(source, sink, INT_MAX)) {totalFlow += pushedFlow;}}// 重置所有边的流量,为下一次计算做准备resetFlow();return totalFlow;}private:/*** 重置图中所有边的流量为 0*/void resetFlow() {for (int i = 0; i < totalNodes; i++) {for (FlowEdge& edge : graph[i]) {edge.currentFlow = 0;}}}
};int main() {cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);int T;cin >> T;for (int cs= 1; cs <= T; cs++) {int cityCount, roadCount;cin >> cityCount >> roadCount;// 初始化 Dinic 算法实例DinicMaxFlow flowSolver(cityCount);// 读取道路信息并构建图for (int i = 0; i < roadCount; i++) {int cityA, cityB, destroyCost;cin >> cityA >> cityB >> destroyCost;cityA--; cityB--;  // 转换为 0-based 索引flowSolver.addUndirectedEdge(cityA, cityB, destroyCost);}// 计算全局最小割int minCutValue = INT_MAX;int sourceNode = 0;  // 固定源点为第一个城市// 枚举所有其他城市作为汇点for (int sinkNode = 1; sinkNode < cityCount; sinkNode++) {minCutValue = min(minCutValue, flowSolver.computeMaxFlow(sourceNode, sinkNode));}// 处理图原本不连通的情况if (minCutValue == INT_MAX) minCutValue = 0;cout << "Case #" << caseNumber << ": " << minCutValue << '\n';}return 0;
}

总结

本题的关键在于将实际问题转化为图论中的最小割问题,并利用最大流最小割定理来求解。通过固定源点、枚举汇点的方法,结合高效的 Dinic\texttt{Dinic}Dinic 最大流算法,我们能够在合理的时间内解决问题。

算法的时间复杂度为 O(n⋅V2E)O(n \cdot V^2E)O(nV2E),空间复杂度为 O(V+E)O(V + E)O(V+E),对于题目给定的数据范围 (n≤150n \leq 150n150) 是完全可行的。

http://www.dtcms.com/a/560835.html

相关文章:

  • 【Linux】版本控制器Git和调试器—gdb/cgdb的使用
  • 怎么把个人做的网站上传到网上wordpress用户名钩子
  • 成都网站排名网站添加邮件发送怎么做
  • Spring AI 极简入门:15分钟集成AI到SpringBoot应用
  • 临潼城市建设局网站外资公司注册
  • 1.基础--数据库相关概念
  • 使用DTS迁移工具迁移oracle到DM
  • langchain agent的短期记忆
  • 使用DrissionPage和自动化技术实现得物鞋子信息爬取
  • 做网站的是干嘛的为网站添加统计
  • 网站设计的建设目的做网站推销手表
  • [LitCTF 2023]Vim yyds
  • LNMP环境部署 KodBox私有云盘
  • 解决windows docker开发thinkphp6启动慢的问题
  • Rust编译参数与优化控制
  • springboot后端的接口headers
  • day04(11.2)——leetcode面试经典150
  • mysql常识和jdbc工具类的进化以及连接池思想
  • 七.Docker网络
  • 怎么做免费视频网站吗网站建设哪儿好
  • 推荐优质wordpress外贸网站主题
  • TDengine 数学函数 ASCII 用户手册
  • @Builder注解导致mybatis类型匹配错误 Cannot determine value type from string
  • AI模型开发 | 从零部署Deepseek OCR模型,零门槛开发PDF文档解析工具
  • Linux INPUT 子系统实验
  • 1000套实习报告模板大合集+多行业多专业实习总结实践报告素材
  • 百度网站快速优化国内flask做的网站
  • Spring AI--Prompt、多轮对话实现方案
  • 网页模板免费下载网站广州页面制作公司
  • Java:继承与多态