网络流算法: Edmonds-Karp算法

图论相关帖子

  • 基本概念
  • 图的表示: 邻接矩阵和邻接表
  • 图的遍历: 深度优先与广度优先
  • 拓扑排序
  • 图的最短路径:Dijkstra算法和Bellman-Ford算法
  • 最小生成树
  • 二分图
  • 多源最短路径
  • 强连通分量
  • 欧拉回路和汉密尔顿回路
  • 网络流算法: Edmonds-Karp算法
  • 网络流算法: Dinic算法

环境要求

本文所用样例在Windows 11以及Ubuntu 24.04上面编译通过.

  1. Windows: 使用[Visual Studio],
  2. Ubuntu: 使用 Clang 18.1.3. (Ubuntu 24.04 系统安装版本)
  3. GCC 无法编译直接本项目代码, 因为本文代码使用了 C++20 Module, 而 GCC 对此支持不完整.

Intro

网络流算法是一类用于解决在流网络中最大化流从源点到汇点问题的算法. 流网络是由节点和有向边构成的图, 每条边有一个容量限制, 表示可以通过该边的最大流量. 网络流问题的目标是找到一种流分配方式, 使得整个网络从源到汇的总流量最大.

在下图中, 节点 0 是源点, 节点 5 是汇点, 最大流问题就是求从源点到汇点的最大流量是多少.

网络流

下面是一种可行的解决方案. 路径权重中, 前者表示实际通过的流量, 后者表示其最大容量.

network capacity

Ford-Fulkerson 方法

Ford-Fulkerson 方法是一种用于解决最大流问题的经典算法, 它通过寻找增广路径(augmenting path)来逐步增加网络中从源点到汇点的流量, 直到无法再找到新的增广路径为止. 这种方法基于的是残量图和增广路径的概念.

残量图(Residual Graph)

残量图是从从网络流的状态衍生出来的. 它给每条边增加了反向边. 每个边的权重会重新调整: 假设其原来最大容量为 a a a, 当前流量为 b b b, 则参量图中这个边的最大容量为 a − b a-b ab, 反向边的流量为 b b b.

以下图为例, 这是一个原始图. 此时没有流量.

origin

下图中, 左侧是当前的流量状态, 而右侧则是残量图的状态.

参量图

我们可以看到:

  • 对于 (0 -> 1) 这条边, 总容量为3,目前容量为3. 所以在残量图中: (0 -> 1) 的最大容量为 3 − 3 = 0 3-3=0 33=0, 反向边(1 - > 0)的容量为 3 3 3.
  • 对于 (1 -> 2) 这条边, 总容量为5, 当前流量为3. 所以在残量图中: (1 -> 2) 的最大容量为 5 − 3 = 2 5-3=2 53=2, 反向边(2 - > 1)的容量为 3 3 3.

数学表述为: 给定一个流网络 G = ( V , E ) G=(V, E) G=(V,E) 以及其上的流 f f f, 残量图 G f Gf Gf 包含了原网络中的每条边, 同时也包括了反向边. 对于原网络中的每条边 ( u , v ) (u, v) (u,v), 如果当前流 f ( u , v ) f(u, v) f(u,v) 小于容量 c ( u , v ) c(u, v) c(u,v), 则在残量图中存在一条从 u u u v v v 的边, 容量为 c f ( u , v ) = c ( u , v ) − f ( u , v ) c_f(u, v) = c(u, v) - f(u, v) cf(u,v)=c(u,v)f(u,v); 同时, 如果 f ( u , v ) > 0 f(u, v) > 0 f(u,v)>0, 则在残量图中也存在一条从 v v v u u u 的边, 容量为 c f ( v , u ) = f ( u , v ) c_f(v, u) = f(u, v) cf(v,u)=f(u,v).

增广路径

在残量图中, 从源 s 到汇 t 的一条路径被称为增广路径, 如果这条路径上所有的边都具有正容量. 沿着这样的路径可以增加流值, 增加量由路径上最小剩余容量决定.

继续以上图为例, 我们可以在参量图中寻找从源点到汇点的路径. 如图, 我们发现了一条:

增广路径

可以看到这条路径可以通过的最大流量为2. 所以我们可以获得2的增量. 对应的流量图为右侧图所示.

Edmonds-Karp 算法

Edmonds-Karp算法是Ford-Fulkerson方法的一种实现, 用于计算网络流图中的最大流. 它特别之处在于使用广度优先搜索(BFS)来寻找增广路径, 即从源节点到汇节点的最短路径(这里的最短是指路径上的边数最少). 这保证了算法的效率和稳定性, 并且使得其时间复杂度为O(VE^2), 其中V是网络中的顶点数, E是边数.

算法步骤

  1. 初始化: 开始时, 所有边的流量都设为0.
  2. 寻找增广路径: 通过BFS寻找从源到汇的最短路径, 这条路径上所有的边必须有剩余容量(即实际容量大于当前流量).
  3. 更新流量: 一旦找到增广路径, 就沿着这条路径增加流量, 直到无法再找到增广路径为止.
  4. 终止条件: 当没有从源到汇的增广路径时, 算法终止, 此时的流量即为最大流.

Edmonds-Karp算法相比于其他实现Ford-Fulkerson方法的变种更加稳定, 因为它总是选择最短的增广路径, 这避免了某些情况下可能出现的低效行为. 此外, 由于其清晰的步骤和逻辑, 它也是学习网络流问题的良好起点.

代码实现

构建残量图
void BuildResidualGraph() {residual_graph_.Reset(graph_.V(), graph_.Directed(), graph_.Weighted());for (Vertex u = 0; u < graph_.V(); u++) {for (Vertex v : graph_.Adj(u)) {residual_graph_.AddEdge(u, v, graph_.GetWeight(u, v));residual_graph_.AddEdge(v, u, 0);}}
}
寻找增广路径
std::vector<WeightedEdge> FindArgumentPath(const AdjList& graph, unsigned src,unsigned dst) {std::vector<unsigned> parent(graph.V(), UINT_MAX);std::vector<bool> visited(graph.V(), false);std::queue<unsigned> q;q.push(src);while (!q.empty()) {auto curr = q.front();q.pop();if (curr == dst) break;if (visited[curr]) continue;visited[curr] = true;for (auto w : graph.Adj(curr)) {if (visited[w]) continue;if (graph.GetWeight(curr, w) <= 0) continue;parent[w] = curr;q.push(w);}}std::vector<WeightedEdge> path;if (parent[dst] == UINT_MAX) return path;int curr = dst;while (parent[curr] != src) {auto begin = parent[curr];auto end = curr;auto weight = graph.GetWeight(begin, end);path.emplace_back(begin, end, weight);curr = begin;}path.emplace_back(src, curr, graph.GetWeight(src, curr));std::reverse(path.begin(), path.end());return path;
}
Edmonds-Karp算法主程序
int MaxFlow(unsigned source, unsigned sink) {BuildResidualGraph();int max_flow = 0;while (true) {auto path = FindArgumentPath(residual_graph_, source, sink);fmt::println("path: {}\n", fmt::join(path, ","));if (path.empty()) break;auto it = std::min_element(path.begin(), path.end(),[](const auto& lhs, const auto& rhs) {return std::get<2>(lhs) < std::get<2>(rhs);});auto flow = std::get<2>(*it);max_flow += flow;for (auto& [u, v, w] : path) {residual_graph_.UpdateWeight(u, v, -flow);residual_graph_.UpdateWeight(v, u, flow);}}return max_flow;
}

完整代码请参考: EdmondsKarp.ixx

Edmonds-Karp Demo
std::vector<graph::WeightedEdge> edges = {std::make_tuple(0, 1, 3), std::make_tuple(0, 2, 2),std::make_tuple(1, 2, 5), std::make_tuple(1, 3, 2),std::make_tuple(2, 3, 3),
};
graph::AdjList wg(4, edges, true);
graph::EdmondsKarp ek(wg);
auto len = ek.MaxFlow(0, 3);
std::cout << "max flow: " << len << "\n";
std::cout << ek.ResidualGraph();

完整代码参考: MaxFlowDemo.cpp

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R语言+AI提示词:贝叶斯广义线性混合效应模型GLMM生物学Meta分析

全文链接&#xff1a;https://tecdat.cn/?p40797 本文旨在帮助0基础或只有简单编程基础的研究学者&#xff0c;通过 AI 的提示词工程&#xff0c;使用 R 语言完成元分析&#xff0c;包括数据处理、模型构建、评估以及结果解读等步骤&#xff08;点击文末“阅读原文”获取完整代…

深度学习简介

目录 一、剖析&#xff0c;什么是深度学习&#xff1f;二、深度学习人工神经网络、机器学习、人工智能关系三、深度学习的发展3.1 从感知机到人工神经网络1. 早期发展2. 陷入低谷3. 短暂复兴4. 再次受挫5. 深度突破 3.2 深度学习时代1. 语音领域突破2. 大规模图像数据库3. Alex…

进行性核上性麻痹患者的生活护理指南

进行性核上性麻痹是一种神经系统退行性疾病&#xff0c;合理的生活护理能有效改善症状&#xff0c;提高生活质量。 居家环境要安全。移除地面杂物&#xff0c;铺设防滑垫&#xff0c;安装扶手&#xff0c;降低跌倒风险。在浴室、厨房等湿滑区域要特别加强防护措施。建议在床边、…

【数据结构】链表与顺序表的比较

链表和顺序表是两种常见的数据结构&#xff0c;各有优缺点&#xff0c;适用于不同的场景。 ### 顺序表&#xff08;数组&#xff09; 顺序表在内存中连续存储元素&#xff0c;支持随机访问。 **优点&#xff1a;** 1. **随机访问**&#xff1a;通过索引直接访问元素&#xf…

osgEarth安装总结

第一步&#xff1a;安装OSG 直接通过git下载源码&#xff0c;使用cmake进行编译&#xff0c; git clone --depth 1 https://github.com/openscenegraph/OpenSceneGraph.git mkdir build cd build cmake .. make sudo make isntall编译过程中缺什么库&#xff0c;就安装什么库 …

网络安全-使用DeepSeek来获取sqlmap的攻击payload

文章目录 概述DeepSeek使用创建示例数据库创建API测试sqlmap部分日志参考 概述 今天来使用DeepSeek做安全测试&#xff0c;看看在有思路的情况下实现的快不快。 DeepSeek使用 我有一个思路&#xff0c;想要测试sqlmap工具如何dump数据库的&#xff1a; 连接mysql数据库&#…

25物理学研究生复试面试问题汇总 物理学专业知识问题很全! 物理学复试全流程攻略 物理学考研复试调剂真题汇总

正在为物理考研复试专业面试发愁的你&#xff0c;是不是不知道从哪开始准备&#xff1f; 学姐告诉你&#xff0c;其实物理考研复试并没有你想象的那么难&#xff01;只要掌握正确的备考方法&#xff0c;稳扎稳打&#xff0c;你也可以轻松拿下高分&#xff01;今天给大家准备了…

KTV点歌系统

收藏关注不迷路&#xff01;&#xff01; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更多…

开源绝版经典小游戏合集

随着生活节奏的日益加快&#xff0c;我们常常需要一些小游戏来缓解疲惫的身心。过去&#xff0c;Windows 7自带的扫雷、蜘蛛纸牌等小游戏深受大家喜爱&#xff0c;但随着系统的更新换代&#xff0c;这些经典游戏逐渐淡出了人们的视野。我也曾花费不少时间寻找这些游戏&#xff…

【AI Coding】Windsurf:【Prompt】全局规则与项目规则「可直接使用」

先看效果 这是在windsurf中与ai对话的反馈 为什么要写这个规则&#xff08;Prompt&#xff09; 写的这份针对windsurf的全局规则&#xff0c;详细的涵盖了前端的各个方向&#xff1a;技术栈、测试、工程、性能优化、回答规范 通过提前预设一些关键词&#xff0c;可以提高回答…

传输层协议TCP

TCP全称为 传输控制协议(Transmission Control Protocol)&#xff0c;就是要对数据的传输进行一个详细的控制。 TCP协议段格式 源端口&#xff1a;发送方的端口号&#xff0c;用来标识发送端的应用程序或进程。 目标端口&#xff1a;接收方的端口号&#xff0c;用来标识接收端…

SpringBoot 日志 与 门面模式(外观模式)

日志的使用 先引入日志对象&#xff0c;注意是 引入的是 org.slf4j 这个包下的 Logger 在传参上&#xff1a;可以传入类名&#xff0c;或者一个字符串&#xff0c;该参数表示日志名称 例如如果传入 “aaaa”&#xff0c;那么日志的名称就是 aaaa RequestMapping("/log&…

【MySQL篇】数据类型

目录 前言&#xff1a; 1&#xff0c;数据类型的分类 ​编辑 2 &#xff0c;数值类型 2.1 tinyint类型 2.2 bit类型 2.3 小数类型 2.3.1 float类型 2.3.2 decimal类型 3&#xff0c;字符串类型 3.1 char 3.2 varchar 3.3 char与varchar的比较 3.4日期和时间类型 3.5 …

网络类型及数据链路层协议

网络类型的分类&#xff1a; p2p----point to point---点到点网络MA---Multi-Access---多点接入网络 BMA--- Broadcast Multi-Access Network ---广播型多点接入网络NBMA--- Non-Broadcast Multi-Access Network ---非广播型多点接入网络 数据链路层协议&#xff1a; MA网络…

序列化选型:字节流抑或字符串

序列化既可以将对象转换为字节流&#xff0c;也可以转换为字符串&#xff0c;具体取决于使用的序列化方式和场景。 转换为字节流 常见工具及原理&#xff1a;在许多编程语言中&#xff0c;都有将对象序列化为字节流的机制。例如 Python 中的 pickle 模块、Java 中的对象序列化…

企业微信里可以使用的企业内刊制作工具,FLBOOK

如何让员工及时了解公司动态、行业资讯、学习专业知识&#xff0c;并有效沉淀企业文化&#xff1f;一份高质量的企业内刊是不可或缺的。现在让我来教你该怎么制作企业内刊吧 1.登录与上传 访问FLBOOK官网&#xff0c;注册账号后上传排版好的文档 2.选择模板 FLBOOK提供了丰富的…

Java 大视界 -- Java 大数据在智能安防入侵检测与行为分析中的应用(108)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

Spring IoC

前言: 我们介绍下Spring. 通过前⾯的学习, 我们知道了Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因. 这么说可能还是很抽象.用一句话概括就是Spring就是一个包含了众多工具和方法的IoC容器. 所…

如何配置虚拟机的IP上网

一.配置vm虚拟机网段 在虚拟机主页点击编辑->虚拟网络编辑器&#xff0c;选择VMnet8&#xff0c;要改动两个地方&#xff1a; 1.子网IP改成192.168.10.0 2.NAT设置->192.168.10.2 让所有的vm配置的虚拟机使用NAT时&#xff0c;它们的网段都是一致的。注意:这里的第三个部…

Java GC 基础知识快速回顾

目录 一、Java 垃圾回收&#xff08;GC&#xff09;基本概念和重要性分析 &#xff08;一&#xff09; Java 垃圾回收&#xff08;GC&#xff09;基本概念回顾 1.GC 三种常见语义 2.Mutator&#xff1a;应用程序的内存管理角色 3.TLAB&#xff08;线程本地分配缓存&#x…