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

网络流算法: Dinic算法

图论相关帖子

  • 基本概念
  • 图的表示: 邻接矩阵和邻接表
  • 图的遍历: 深度优先与广度优先
  • 拓扑排序
  • 图的最短路径: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

Dinic 算法是一种用于解决最大流问题的高效算法, 它基于 Ford-Fulkerson 方法, 并通过引入层次图(level graph)的概念来加速寻找增广路径的过程. 我们先从层次图的概念开始.

层次图(Level Graph)

层次图是一个特殊的网络, 它通过引入层次值来表示节点之间的距离. 层次值从源点开始, 按照广度优先搜索(BFS)的顺序进行更新, 直到所有节点都被更新.

构建层次图的过程主要是通过从源点开始进行一次广度优先搜索(BFS), 根据节点到源点的距离来为每个节点分配层次值(level). 以下是构建层次图的具体步骤:

构建层次图的步骤

  1. 初始化:

    • 设定源点的层次值为 0.
    • 创建一个队列, 并将源点加入队列中.
  2. 执行广度优先搜索(BFS):

    • 从队列中取出一个节点 u u u.
    • 对于 u u u 的所有邻接节点 v v v, 如果边 ( u , v ) (u, v) (u,v) 在残量图中仍有剩余容量(即该边未被完全利用), 并且 v v v 还未被访问过(或其层次值尚未确定), 则:
      • 设置 v v v 的层次值为 u u u 的层次值加 1, 表示 v v v u u u “远” 一个单位距离.
      • v v v 加入队列中, 以便后续处理 v v v 的邻接节点.
    • 如果到达汇点, 则停止搜索; 否则继续直到队列为空.
  3. 结束条件:

    • 当队列为空时, 说明已经遍历了所有可到达的节点, 并且为这些节点分配了层次值. 此时, 如果汇点没有被访问到(即它没有层次值), 意味着无法再找到从源点到汇点的任何路径, 算法可以终止.
    • 如果汇点有层次值, 则层次图构建完成, 可以进行下一步的深度优先搜索(DFS)以寻找增广路径.

层次图构建样例

  1. 初始化. 给定一个原始图, 右侧是它的层次图初始状态.

    Level Graph Init

  2. 从源点(0)开始出发, 第一层接触到13. 将边(0, 1),(0, 3)加入到层次图中.

    Level Graph 1

  3. 接下来从13开始, 第二层接触到24. 将边(1, 2),(3, 2),(3, 4)加入到层次图中.

    Level Graph 2

  4. 最后从24开始, 第三层接触到5, 将边(2,5), (4,5)加入到层次图中.
    Level Graph 3

  5. 由于5是汇点, 所以算法结束. 最后的结果如下:

    Level Graph 4

层次图的作用

  • 限制搜索范围: 层次图只包含那些按照一定顺序(即按层次值从小到大)排列的节点和边, 这使得在寻找增广路径时只需考虑特定的边集, 减少了不必要的搜索.
  • 加快增广路径的查找速度: 由于 DFS 仅沿着层次图中的边进行搜索, 这样可以在较短的时间内找到一条或多条增广路径, 从而提高整个算法的效率.

通过以上步骤, Dinic 算法能够有效地构建层次图, 并利用这个结构快速找到增广路径, 最终求解最大流问题. 这种方法不仅提高了算法的性能, 而且使其实现更加直观易懂.

Dinic 算法步骤

Dinic 算法步骤

  1. 构建层次图: 从源点开始进行广度优先搜索(BFS), 为每个节点分配一个层次值(level), 这个值表示该节点到源点的距离. 只有当流量可以通过一系列边从一个较低层次的节点流向较高层次的节点时, 这条路径才会被纳入层次图中.

  2. 寻找增广路径: 使用深度优先搜索(DFS)在层次图中寻找从源点到汇点的增广路径. 由于层次图的性质, DFS 可以更高效地找到这些路径.

  3. 更新残量图: 一旦找到了一条增广路径, 就沿着这条路径调整流量, 同时更新相应的残量图. 这包括增加正向边的流量并减少反向边的容量.

  4. 重复步骤: 重复上述步骤, 直到无法再找到从源点到汇点的增广路径为止.

Dinic 算法样例

  1. 初始化: 给定一个原始图如图左侧所示, 如果我们省去容量为 0 的边, 那么左侧图可以看做是一个残量图, 右侧是它的层次图初始状态.

    dinic init

  2. 利用 DFS 寻找一条增广路径, 我们找到的了一条, 如图所示. 它的容量为 8, 接着我们更新残量图, 得到如图右边所示的图. 注意: 此时 1 -> 2 的边容量为 0, 故而省去.

    dinit 1

  3. 我们继续从残量图获取层级图. 如下:

    dinic-2

  4. 继续寻找增广路径, 找到一条, 如图所示, 容量为 3. 它的容量为 3, 接着我们更新残量图, 得到如图右边所示的图. 注意: 此时 3 -> 4 的边容量为 0, 故而省去.

    dinic 3

  5. 我们继续从残量图获取层级图. 如下:

    dinic 4

  6. 继续寻找增广路径, 找到一条, 如图所示, 容量为 1. 接着我们更新残量图, 得到如图右边所示的图. 注意: 此时 3 -> 2 的边容量为 0, 故而省去.

    dinic 5

  7. 我们继续从残量图获取层级图. 如下:

    dinic 6

  8. 继续寻找增广路径, 找不到了, 所以算法结束.

特点和优势:

  • 时间复杂度: Dinic 算法的时间复杂度为 O ( V 2 E ) O(V^2 E) O(V2E), 其中 V V V 是顶点数, E E E 是边数. 对于稠密图, 其性能优于 Edmonds-Karp 算法.
  • 阻塞流: Dinic 算法每次迭代都会尝试在当前层次图中找到一个"阻塞流", 即无法再在这个层次图中找到任何增广路径. 这种策略使得算法效率更高.
  • 应用广泛: 除了用于计算最大流之外, Dinic 算法还常用于解决二分图匹配等问题.

C++ 代码实现

class Dinic {public:explicit Dinic(const AdjList& graph): graph_(graph), residual_graph_(graph.V(), true, true) {BuildResidualGraph();}int MaxFlow(int src, int dst) {int max_flow = 0;while (true) {auto level = BuildLevelGraph(src);auto path = FindArgumentPath(level, src, dst);fmt::println("current path: {}", 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);});int flow = std::get<2>(*it);if (flow <= 0) {break;}max_flow += flow;for (auto& [u, v, w] : path) {residual_graph_.UpdateWeight(u, v, -flow);residual_graph_.UpdateWeight(v, u, flow);}}return max_flow;}void BuildResidualGraph() {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);}}}AdjList BuildLevelGraph(unsigned src) {AdjList g(graph_.V(), true, true);std::queue<unsigned> q;q.push(src);while (!q.empty()) {auto u = q.front();q.pop();for (auto v : residual_graph_.Adj(u)) {int w = residual_graph_.GetWeight(u, v);if (w <= 0 || g.HasEdge(u, v)) {continue;}g.AddEdge(u, v, w);q.push(v);}}return g;}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;}private:const AdjList& graph_;AdjList residual_graph_;
};

代码源文件链接在此: Dinic.ixx

相关文章:

网络流算法: Dinic算法

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

【Godot4.3】自定义简易菜单栏节点ETDMenuBar

概述 Godot中的菜单创建是一个复杂的灾难性工作&#xff0c;往往无从下手&#xff0c;我也是不止一次尝试简化菜单的创建。 从自己去年的发明“简易树形数据”用于简化Tree控件获得灵感&#xff0c;于是尝试编写了用于表示菜单数据的EasyMenuData类&#xff0c;以及对应的纯文…...

如何杀死僵尸进程?没有那个进程?

在题主跑代码的时候遇到了这样一种很奇怪的问题&#xff1a; 可以看到显卡0没有跑任何程序但是还是被占据着大量显存&#xff0c;这种进程称为“僵尸进程”&#xff0c;并且当我想kill它的时候&#xff0c;出现下面这种情况&#xff1a; 查过各种资料&#xff0c;最后我的解决…...

Solana 核心概念全解析:账户、交易、合约与租约,高流量区块链技术揭秘!

目录 1.Solana 核心概念简述 1.1. 账户&#xff08;Account&#xff09; 1.2. 交易&#xff08;Transaction&#xff09; 1.3. 交易指令&#xff08;Instruction&#xff09; 1.4. SPL 代币 1.5. 合约&#xff08;Program&#xff09; 1.6. 租约&#xff08;Rent&#x…...

Leetcode-853. Car Fleet [C++][Java]

目录 一、题目描述 二、解题思路 Leetcode-853. Car Fleethttps://leetcode.com/problems/car-fleet/description/ 一、题目描述 There are n cars at given miles away from the starting mile 0, traveling to reach the mile target. You are given two integer array …...

012 rocketmq事务消息

文章目录 事务消息概念介绍交互流程事务消息原理TransactionListener接⼝TransactionProducer.javaTransactionConsumer.java 事务消息 内置topic中的消息对消费者不可见 本地事务mq消息事务消息 消息队列 RocketMQ 版提供的分布式事务消息适⽤于所有对数据最终⼀致性有强需求…...

ChatGPT与DeepSeek:开源与闭源的AI模型之争

目录 一、模型架构与技术原理 二、性能能力与应用场景 三、用户体验与部署灵活性 四、成本与商业模式 五、未来展望与市场影响 六、总结 随着人工智能技术的飞速发展&#xff0c;ChatGPT和DeepSeek作为两大领先的AI语言模型&#xff0c;成为了行业内外关注的焦点。它们在…...

Ollama的底层实现原理分析

一、背景 Ollama我们可以很方便的对DeepSeek等开源大模型进行部署&#xff0c;几条命令便能部署一个本地大模型服务&#xff0c;降低了非专业大模型开发者的门槛。 我们从中可以看到类似Docker的影子&#xff0c;ollama run 、ollama list等等&#xff0c;拉取对应大模型镜像&a…...

nginx 动态计算拦截非法访问ip

需求&#xff1a;在Nginx上实现一个动态拦截IP的方法&#xff0c;具体是当某个IP在1分钟内访问超过60次时&#xff0c;将其加入Redis并拦截&#xff0c;拦截时间默认1天。 技术选型&#xff1a;使用NginxLuaRedis的方法。这种方案通过Lua脚本在Nginx处理请求时检查Redis中的黑…...

商业秘密维权有哪些成本开支?

企业商业秘密百问百答之六十三&#xff1a;商业秘密维权费用项目有哪些&#xff1f; 在商业秘密维权过程中&#xff0c;原告可能需要支付多种费用&#xff0c;一般费用项目包括&#xff1a; 1、诉讼费。诉讼费是向法院支付的费用&#xff0c;包括起诉费、案件受理费等。这些费…...

使用UA-SPEECH和TORGO数据库验证自动构音障碍语音分类方法

使用UA-SPEECH和TORGO数据库验证自动构音障碍语音分类方法 引言 原文:On using the UA-Speech and TORGO databases to validate automatic dysarthric speech classification approaches 构音障碍简介 构音障碍是一种由于脑损伤或神经疾病(如脑瘫、肌萎缩侧索硬化症、帕金森…...

WebSocketHandler 是 Spring Framework 中用于处理 WebSocket 通信的接口

WebSocketHandler 是 Spring Framework 中用于处理 WebSocket 通信的接口&#xff0c;其主要作用是定义了如何处理 WebSocket 的各种事件和消息。以下是 WebSocketHandler 的主要作用和功能&#xff1a; ### 1. 处理 WebSocket 生命周期事件 WebSocketHandler 定义了多个方法来…...

Pikachu

一、网站搭建 同样的&#xff0c;先下载安装好phpstudy 然后启动Apache和Mysql 然后下载pikachu&#xff0c;解压到phpstudy文件夹下的www文件 然后用vscode打开pikachu中www文件夹下inc中的config.inc.php 将账户和密码改为和phpstudy中的一致&#xff08;默认都是root&…...

如何使用 Jenkins 实现 CI/CD 流水线:从零开始搭建自动化部署流程

如何使用 Jenkins 实现 CI/CD 流水线:从零开始搭建自动化部署流程 在软件开发过程中,持续集成(CI)和持续交付(CD)已经成为现代开发和运维的标准实践。随着代码的迭代越来越频繁,传统的手动部署方式不仅低效,而且容易出错。为了提高开发效率和代码质量,Jenkins作为一款…...

Vue.js 学习笔记

文章目录 前言一、Vue.js 基础概念1.1 Vue.js 简介1.2 Vue.js 的特点1.3 Vue.js 基础示例 二、Vue.js 常用指令2.1 双向数据绑定&#xff08;v-model&#xff09;2.2 条件渲染&#xff08;v-if 和 v-show&#xff09;2.3 列表渲染&#xff08;v-for&#xff09;2.4 事件处理&am…...

数据存储:一文掌握RabbitMQ的详细使用

文章目录 一、RabbitMQ简介二、RabbitMQ的概述2.1 基本概念2.2 实际应用场景三、RabbitMQ的安装与配置3.1 安装RabbitMQ3.2 启用管理插件四、使用Python操作RabbitMQ4.1 安装Pika库4.2 生产者示例4.3 消费者示例4.4 发布/订阅模式示例五、RabbitMQ的高级特性5.1 消息持久化5.2 …...

辛格迪客户案例 | 祐儿医药科技GMP培训管理(TMS)项目

01 项目背景&#xff1a;顺应行业趋势&#xff0c;弥补管理短板 随着医药科技行业的快速发展&#xff0c;相关法规和标准不断更新&#xff0c;对企业的质量管理和人员培训提出了更高要求。祐儿医药科技有限公司&#xff08;以下简称“祐儿医药”&#xff09;作为一家专注于创新…...

FreeRtos实时系统: 十六.tickless低功耗模式

FreeRtos实时系统: 十六.tickless低功耗模式 一.tickless低功耗模式简介二.tickless模式详解三.tickless模式相关配置项四.tickless低功耗模式实验五.课堂总结 一.tickless低功耗模式简介 STM32低功耗模式&#xff1a; 二.tickless模式详解 为了可以降低功耗&#xff0c;又不…...

CSDN博客:Markdown编辑语法教程总结教程(上)

❤个人主页&#xff1a;折枝寄北的博客 Markdown编辑语法教程总结 前言1. CSDN Markdown编辑器功能简介1.1 基础操作界面1.2 创作助手和语法说明 2. Markdown编辑器语法2.1 目录2.2 标题2.2.1 标题级别设置2.2.2 标题居中 3. 文本样式3.1 强调文本&#xff08;斜体&#xff09…...

多个pdf合并成一个pdf的方法

将多个PDF文件合并优点&#xff1a; 能更容易地对其进行归档和备份.打印时可以选择双面打印&#xff0c;减少纸张的浪费。比如把住宿发票以及滴滴发票、行程单等生成一个pdf&#xff0c;双面打印或者无纸化办公情况下直接发送给财务进行存档。 方法: 利用PDF24 Tools网站 …...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...