网络流基本概念及实现算法

基本概念

流网络

在这里插入图片描述
对于一个有向图, 抽象成水管里的水的模型, 每根管子有容量限制, 计为 G = ( V , E ) G = (V, E) G=(V,E), 首先不考虑反向边

在这里插入图片描述
对于任意无向图, 都可以将反向边转化为上述形式

如果一条边不存在, 定义为容量为 0 0 0, 形式上来说就是 c ( u , v ) = 0 c(u, v) = 0 c(u,v)=0

可行流

对于每一个流网络考虑一个可行流 f f f, 指定每条边的流量, 需要满足两个条件

  • 容量限制 0 ≤ f ( u , v ) ≤ c ( u , v ) 0 \le f(u, v) \le c(u, v) 0f(u,v)c(u,v)
  • 流量守恒, 对于每个点来说, 流进去多少, 流出来多少, 形式化来说

∑ ( v , u ) f ( v , u ) = ∑ ( u , v ) f ( u , v ) \sum_{(v, u)} f(v, u) = \sum _{(u, v)} f(u, v) (v,u)f(v,u)=(u,v)f(u,v)
对于一个可行流, 从源点流向汇点的流量定义为 ∣ f ∣ |f| f, 每秒从源点流出的流量, 或者流入汇点的流量, 每秒净往外流出的流量

∣ f ∣ = ∑ ( s , v ) f ( s , v ) − ∑ ( v , s ) f ( v , s ) |f| = \sum _{(s, v)} f(s, v) - \sum _{(v, s)} f(v, s) f=(s,v)f(s,v)(v,s)f(v,s)

最大流指的是流量值最大的可行流

残存网络

残存网络是对于流网络某一个可行流
在这里插入图片描述
对于某个可行流 f 1 f_1 f1, 残存网络 G f 1 G_{f_1} Gf1
残存网络的点集与原图完全一致 V f = V V_f = V Vf=V, 但是边集不仅包含原图的边还包含原图的反向边 E f = E + E ′ E_f =E + E ' Ef=E+E

残存网络也是流网络

对于残存网络的容量记为 c ′ ( u , v ) c'(u, v) c(u,v)

  • 对于原图的边, 那么 c ′ ( u , v ) = c ( u , v ) − f ( u , v ) c'(u, v) = c(u, v) - f(u, v) c(u,v)=c(u,v)f(u,v)
  • 对于原图的反向边, c ′ ( u , v ) = f ( v , u ) c'(u, v) = f(v, u) c(u,v)=f(v,u)

对于任意一个可行流 f f f, 都可以求出残存网络 G f G_{f} Gf

记残存网络的可行流 f ′ f' f, f + f ′ f + f' f+f也是原来流网络 G G G的一个可行流

新的流的流量值等于两个流的流量值之和 ∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ |f + f'| = |f| + |f'| f+f=f+f, 流量相加等同于每条边相加

为什么新的流是原流网络 G G G的可行流?

  • 是否满足容量限制
  • 是否满足流量守恒

增广路径

残存网络中, 从源点出发沿着**容量大于 0 0 0**的点走如果能走到汇点, 那么这一条路径就是增广路径
在这里插入图片描述

上述红色路径就是增广路径, 增广路径一定是一个可行流, 流量大于 0 0 0
如果 G f G_f Gf总不存在增广路径, 断言 f f f是原来流网络的最大流

将点集 V V V分为两个集合 S , T S, T S,T, 满足以下条件

  • s ∈ S s \in S sS, t ∈ T t \in T tT
  • S ∪ T = V S \cup T = V ST=V, S ∩ T = ∅ S \cap T = \emptyset ST=

在这里插入图片描述
上述就是割的形式化描述

割的容量

在这里插入图片描述
所有从 S S S指向 T T T的边, 被称为割的容量, 容量不考虑回来的边

c ( S , T ) = ∑ u ∈ S , v ∈ T c ( u , v ) c(S, T) = \sum _{u\in S, v \in T} c(u, v) c(S,T)=uS,vTc(u,v)

割的流量

所有从 S S S留到 T T T的流量减去从 T T T流向 S S S的流量, 也就是净流量

f ( S , T ) = ∑ u ∈ S , v ∈ T f ( u , v ) − ∑ u ∈ T , v ∈ S f ( u , v ) f(S, T) = \sum _{u \in S, v \in T} f(u, v) - \sum _{u \in T, v \in S} f(u, v) f(S,T)=uS,vTf(u,v)uT,vSf(u,v)

对于一个流网络来说, 如果流网络确定, 那么割的容量确定, 但是因为一个流网络存在多个可行流, 因此割的流量是取决于每个可行流的流量的

性质1: 最小割指的是最小割的容量, 对于任意一个割以及任意一个可行流, 割的流量小于等于割的容量, 形式化来说 f ( S , T ) ≤ c ( S , T ) f(S, T) \le c(S, T) f(S,T)c(S,T), 证明过程如下
在这里插入图片描述
性质2: 对于流网络的任意一个割和任意一个可行流都有 f ( S , T ) = ∣ f ∣ f(S, T)= |f| f(S,T)=f

根据性质1和性质2推出 ∣ f ∣ = f ( S , T ) ≤ c ( S , T ) |f| = f(S, T) \le c(S, T) f=f(S,T)c(S,T), 也就推出 ∣ f ∣ ≤ c ( S , T ) |f| \le c(S, T) fc(S,T), 也就是最大流小于等于最小割

最大流最小割定理

对于某个流网络 G = ( V , E ) G = (V, E) G=(V,E), 以下三个结论相互等价

  1. 可行流 f f f是最大流
  2. f f f的残存网络中不存在增广路径
  3. 存在割 [ S , T ] [S, T] [S,T], 使得 c ( S , T ) = ∣ f ∣ c(S, T) = |f| c(S,T)=f

证明如下

1 1 1 2 2 2
反证法, 存在增广路径, 由上述增广路径定理可知, 当前可行流不是最大流

3 3 3 1 1 1
因为 ∣ f ∣ ≤ c ( S , T ) |f| \le c(S, T) fc(S,T), 对于结论 3 3 3, 存在一个割使得 ∣ f ∣ = c ( S , T ) |f| = c(S, T) f=c(S,T), 证明 f f f是否是最大流
最大流 ≥ ∣ f ∣ 最大流 \ge |f| 最大流f, 又因为 ∣ f ∣ = c ( S , T ) ≥ 最大流 |f| = c(S, T) \ge 最大流 f=c(S,T)最大流, 因此 f f f是最大流

由结论 3 3 3得知, 最小割 ≤ c ( S , T ) = ∣ f ∣ ≤ 最大流 最小割 \le c(S, T) = |f| \le 最大流 最小割c(S,T)=f最大流, 也就有最小割小于等于最大流, 上述性质2可知, 最大流小于等于最小割, 因此最大流等于最小割

2 2 2 3 3 3
如果当前残存网络不存在增广路径, 能否构造出一个, 使得 c ( S , T ) = ∣ f ∣ c(S, T) = |f| c(S,T)=f
构造一个合法的割
点集 S S S是在 G f G_f Gf s s s沿着容量大于 0 0 0的边走, 走到的所有点, 因为不存在增广路径, 因此 s s s走不到 t t t
点集 T T T V − S V - S VS
借助的 G f G_f Gf构造的是原网络 G G G里的割

判断当前割的容量是否等于 ∣ f ∣ |f| f
在这里插入图片描述
构造的割的性质如下

  1. f ( x , y ) = c ( x , y ) f(x, y) = c(x, y) f(x,y)=c(x,y)
  2. f ( a , b ) = 0 f(a, b) = 0 f(a,b)=0

对于性质1, 如果 f ( x , y ) < c ( x , y ) f(x, y) < c(x, y) f(x,y)<c(x,y), 那么在残存网络中仍然会有 x x x连到 y y y的边, 也就 x x x能遍历到, y ∈ S y \in S yS, 与我们构造的矛盾
对于性质2, 如果 f ( a , b ) > 0 f(a, b) > 0 f(a,b)>0, 那么在残存网络中也是能遍历到, a ∈ S a \in S aS, 与我们构造的矛盾

∣ f ∣ = ∑ u ∈ S , v ∈ T f ( u , v ) − ∑ u ∈ T , v ∈ S f ( u , v ) |f| = \sum _{u \in S, v \in T} f(u, v) - \sum _{u \in T, v \in S} f(u, v) f=uS,vTf(u,v)uT,vSf(u,v)

因为没有反向边, 并且每个边的流量等于边的容量, 因此

∣ f ∣ = ∑ u ∈ S , v ∈ T c ( u , v ) = c ( S , T ) |f| = \sum _{u \in S, v \in T} c(u, v) = c(S, T) f=uS,vTc(u,v)=c(S,T)

最大流最小割定理证毕

最大流算法实现

F o r d – F u l k e r s o n Ford–Fulkerson FordFulkerson方法

求最大流的贪心方法, 维护残存网络, 不断的在残存网络中寻找增广路径, 将当前流变为 f + f ′ f + f' f+f, 然后在新的流的残存网络中继续寻找增广路径, 将当前残存网络 G f G_f Gf变为 G f + f ′ G_{f + f'} Gf+f

在这里插入图片描述
上图是残存网络中更新的过程, k k k是路径的流量, 也就是所有边容量的最小值, 然后更新所有边的容量

E d m o n d s – K a r p Edmonds–Karp EdmondsKarp算法求最大流

时间复杂度 O ( n m 2 ) O(nm ^ 2) O(nm2)

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;const int N = 1010, M = 20010, INF = 0x3f3f3f3f;int n, m, s_node, t_node;
int head[N], edge_end[M], next_edge[M], w[M], edge_index;
int q[N], pre[N], min_val[N];
bool vis[N];void add(int ver1, int ver2, int val) {edge_end[edge_index] = ver2, next_edge[edge_index] = head[ver1], w[edge_index] = val, head[ver1] = edge_index++;
}bool bfs() {memset(vis, false, sizeof vis);int h = 0, t = -1;q[++t] = s_node;vis[s_node] = true;min_val[s_node] = INF;while (h <= t) {int u = q[h++];for (int i = head[u]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (!vis[ver] && w[i]) {vis[ver] = true;min_val[ver] = min(min_val[u], w[i]);pre[ver] = i;if (ver == t_node) return true;q[++t] = ver;}}}return false;
}int edmonds_karp() {int res = 0;while (bfs()) {int val = min_val[t_node];res += val;for (int i = t_node; i != s_node; i = edge_end[pre[i] ^ 1]) {w[pre[i]] -= val;w[pre[i] ^ 1] += val;}}return res;
}int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);memset(head, -1, sizeof head);cin >> n >> m >> s_node >> t_node;while (m--) {int u, v, w;cin >> u >> v >> w;add(u, v, w);add(v, u, 0);}int res = edmonds_karp();cout << res << "\n";return 0;
}

D i n i c Dinic Dinic算法求最大流

将所有能够增广的路径全部计算, 因为可能有环, 因此使用分层图优化, 路径只能从前一层走到后一层
分层图 + 当前弧优化
时间复杂度 O ( n 2 m ) O(n ^ 2m) O(n2m)

在这里插入图片描述
从起点到终点, 可以流 l i m i t limit limit的流量, 搜索从当前点开始到终点能流的流量

在这里插入图片描述
假设在搜索到终点后流量 f i < l i m i t f_i < limit fi<limit, 说明 f i f_i fi一定会满流, 那么在下一次搜索的时候不需要搜索该边

在这里插入图片描述
而是从下一条边开始搜, 代码表示为 c u r r [ u ] = i curr[u] = i curr[u]=i

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;const int N = 10010, M = 200010, INF = 0x3f3f3f3f;int n, m, s_node, e_node;
int head[N], edge_end[M], next_edge[M], w[M], edge_index;
int q[N], layer[N], curr[N];void add(int ver1, int ver2, int val) {edge_end[edge_index] = ver2, next_edge[edge_index] = head[ver1], w[edge_index] = val, head[ver1] = edge_index++;
}bool bfs() {memset(layer, -1, sizeof layer);int h = 0, t = -1;q[++t] = s_node;layer[s_node] = 0;curr[s_node] = head[s_node];while (h <= t) {int u = q[h++];for (int i = head[u]; ~i; i = next_edge[i]) {int ver = edge_end[i];if (layer[ver] == -1 && w[i]) {layer[ver] = layer[u] + 1;curr[ver] = head[ver];if (ver == e_node) return true;q[++t] = ver;}}}return false;
}int dfs(int u, int limit) {if (u == e_node) return limit;// 从当前点向后流的流量int flow = 0;for (int i = curr[u]; ~i && flow < limit; i = next_edge[i]) {int ver = edge_end[i];// i前面的边都用完了, 当前弧更新为icurr[u] = i;if (layer[ver] == layer[u] + 1 && w[i]) {int val = dfs(ver, min(w[i], limit - flow));if (!val) layer[ver] = -1;w[i] -= val;w[i ^ 1] += val;flow += val;}}return flow;
}int dinic() {int res = 0, flow;while (bfs()) {// 搜索增广路径并且累计全部的流量while ((flow = dfs(s_node, INF))) {res += flow;}}return res;
}int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);memset(head, -1, sizeof head);cin >> n >> m >> s_node >> e_node;while (m--) {int u, v, w;cin >> u >> v >> w;add(u, v, w);add(v, u, 0);}int res = dinic();cout << res << "\n";return 0;
}

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

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

相关文章

【css酷炫效果】纯CSS实现球形阴影效果

【css酷炫效果】纯CSS实现球形阴影效果 缘创作背景html结构css样式完整代码基础版进阶版(动态版) 效果图 想直接拿走的老板&#xff0c;链接放在这里&#xff1a;上传后更新 缘 创作随缘&#xff0c;不定时更新。 创作背景 刚看到csdn出活动了&#xff0c;赶时间&#xff0…

Linux如何在设备树中表示和引用设备信息

DTS基本知识 dts 硬件的相应信息都会写在.dts为后缀的文件中&#xff0c;每一款硬件可以单独写一份xxxx.dts&#xff0c;一般在Linux源码中存在大量的dts文件&#xff0c;对于arm架构可以在arch/arm/boot/dts找到相应的dts&#xff0c;一个dts文件对应一个ARM的machie。 dtsi 值…

【数学建模】模糊综合评价模型详解、模糊集合论简介

模糊综合评价模型详解 文章目录 模糊综合评价模型详解1. 模糊综合评价模型概述2. 模糊综合评价的基本原理2.1 基本概念2.2 评价步骤 3. 模糊综合评价的数学模型3.1 数学表达3.2 模糊合成运算 4. 模糊综合评价的应用领域5. 模糊综合评价的优缺点5.1 优点5.2 缺点 6. 模糊综合评价…

C++20 中的同步输出流:`std::basic_osyncstream` 深入解析与应用实践

文章目录 一、std::basic_osyncstream 的背景与动机二、std::basic_osyncstream 的基本原理三、std::basic_osyncstream 的使用方法&#xff08;一&#xff09;基本用法&#xff08;二&#xff09;多线程环境下的使用&#xff08;三&#xff09;与文件流的结合 四、std::basic_…

C/C++蓝桥杯算法真题打卡(Day8)

一、P8780 [蓝桥杯 2022 省 B] 刷题统计 - 洛谷 算法代码&#xff1a; #include<bits/stdc.h> // 包含标准库中的所有头文件&#xff0c;方便使用各种数据结构和算法 using namespace std; // 使用标准命名空间&#xff0c;避免每次调用标准库函数时都要加 std::in…

JavaScript 编程:从基础到高级应用的全面探索

引言 JavaScript 作为一种广泛应用于 Web 开发的脚本语言&#xff0c;已经成为现代互联网不可或缺的一部分。它不仅可以为网页增添交互性和动态效果&#xff0c;还能在服务器端&#xff08;如 Node.js&#xff09;进行后端开发。本文将从 JavaScript 的基础语法开始&#xff0…

第十三次CCF-CSP认证(含C++源码)

第十三次CCF-CSP认证 跳一跳满分题解 碰撞的小球满分题解遇到的问题 棋局评估满分题解 跳一跳 题目链接 满分题解 没什么好说的 基本思路就是如何用代码翻译题目所给的一些限制&#xff0c;以及变量应该如何更新&#xff0c;没像往常一样给一个n&#xff0c;怎么读入数据&…

Pytorch使用手册—自定义函数的双重反向传播与自定义函数融合卷积和批归一化(专题五十二)

1. 使用自定义函数的双重反向传播 有时候,在反向计算图中运行两次反向传播是有用的,例如计算高阶梯度。然而,支持双重反向传播需要对自动求导(autograd)有一定的理解,并且需要小心处理。支持单次反向传播的函数不一定能够支持双重反向传播。在本教程中,我们将展示如何编…

MySQL:数据库基础

数据库基础 1.什么是数据库&#xff1f;2.为什么要学习数据库&#xff1f;3.主流的数据库&#xff08;了解&#xff09;4.服务器&#xff0c;数据库&#xff0c;表之间的关系5.数据的逻辑存储6.MYSQL架构7.存储引擎 1.什么是数据库&#xff1f; 数据库(Database,简称DB)&#x…

Web Component 教程(五):从 Lit-html 到 LitElement,简化组件开发

前言 在现代前端开发中&#xff0c;Web 组件是一种非常流行的技术&#xff0c;它允许我们创建可重用的、自包含的 UI 元素。而 Lit-html 是一个简洁高效库&#xff0c;用于在 Web 组件中进行渲染。在这篇教程中&#xff0c;我们一步步学习如何 Lit-html 来创建 Web Component。…

【C++】二叉树和堆的链式结构(上)

本篇博客给大家带来的是用C语言来实现堆链式结构和二叉树的实现&#xff01; &#x1f41f;&#x1f41f;文章专栏&#xff1a;数据结构 &#x1f680;&#x1f680;若有问题评论区下讨论&#xff0c;我会及时回答 ❤❤欢迎大家点赞、收藏、分享&#xff01; 今日思想&#xff…

Devops之AWS:如何安装AWS CLI

AWS 命令行界面&#xff08;AWS CLI&#xff09;是一种开源工具&#xff0c;让我们能够使用命令行 Shell 中的命令与 AWS 服务进行交互。 安装步骤&#xff1a; 下载并运行AWS CLI的MSI安装程序&#xff1a; 点击如下的链接&#xff0c;即可下载MSI安装程序&#xff1a; htt…

PH2D数据集: 用人类演示数据提升人形机器人操作能力,助力跨实体学习

2025-03-18, 由加州大学圣地亚哥分校, 卡内基梅隆大学, 华盛顿大学, 麻省理工学院等机构联合收集了PH2D数据集。该数据集包含26824个任务导向的人类演示&#xff0c;采用消费者级VR设备收集&#xff0c;提供了准确的3D手部关键点姿态和语言注释。数据集覆盖了多种操作任务、不同…

python 数据可视化matplotib库安装与使用

要使用 matplotlib 库进行数据可视化&#xff0c;首先你需要确保已经安装了该库。如果你还没有安装&#xff0c;可以通过 Python 的包管理器 pip 来安装它。在你的命令行工具中运行以下命令来安装 matplotlib&#xff1a; pip install matplotlib安装完成后&#xff0c;你就可以…

【MySQL基础-10】MySQL中的LENGTH()函数:用法详解与实例分析

在MySQL数据库中&#xff0c;LENGTH()函数是一个非常常用的字符串函数&#xff0c;用于计算字符串的字节长度。理解并掌握LENGTH()函数的用法&#xff0c;对于处理字符串数据、优化查询以及进行数据验证都非常有帮助。本文将详细介绍LENGTH()函数的用法&#xff0c;并通过实例演…

Matlab 基于专家pid控制的时滞系统

1、内容简介 Matlab 185-基于专家pid控制的时滞系统 可以交流、咨询、答疑 2、内容说明 略 在处理时滞系统&#xff08;Time Delay Systems&#xff09;时&#xff0c;使用传统的PID控制可能会面临挑战&#xff0c;因为时滞会导致系统的不稳定或性能下降。专家PID控制通过结…

E902基于bash与VCS的仿真环境建立

网上看见很多E902仿真的文章&#xff0c;但用到的编译器是类似于这种Xuantie-900-gcc-elf-newlib-x86_64-V3.0.1-20241120&#xff0c;而我按照相应的步骤与对应的编译器&#xff0c;仿真总会报错。后面将编译器换成riscv64-elf-x86_64-20210512&#xff0c;反而成功了。现在开…

SpringSecurity配置(自定义认证过滤器)

文末有本篇文章的项目源码文件可供下载学习 在这个案例中,我们已经实现了自定义登录URI的操作,登录成功之后,我们再次访问后端中的API的时候要在请求头中携带token,此时的token是jwt字符串,我们需要将该jwt字符串进行解析,查看解析后的User对象是否处于登录状态.登录状态下,将…

《UNIX网络编程卷1:套接字联网API》第1章 简介

《UNIX网络编程卷1&#xff1a;套接字联网API》第1章 简介 1.1 网络编程的核心价值与挑战 网络编程是实现跨设备通信的技术基础&#xff0c;其核心目标是通过协议栈实现数据的可靠传输与高效交换。在嵌入式系统、云计算、物联网等领域&#xff0c;网络编程能力直接决定了系统的…

D-Wave专用量子计算机登顶Science 率先展示在真实场景中的量子优势(内附下载)

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨浪味仙 行业动向&#xff1a;4200字丨16分钟阅读 摘要&#xff1a;加拿大专用量子计算机公司 D-Wave 在 Science 期刊发表了论文&#xff0c;题为《Beyond-Classical Compu…