论网络流(最大流篇)--新手入门超详解--包教包会
论网络流--新手入门超详解--包教包会
- 1 前言
- 2 什么是最大流
- 3最大流问题的求解
- (1)问题转化--增广路的引入
- (2)走回头路--EK算法
- (3)EK的弊端
- (4)化图为树--DINIC算法
- 4后记
1 前言
网络流是图论算法,阅读本文需要以下前置知识
1搜索算法(dfs+bfs)
2图的存储
3最短路径算法(dijkstra,floyd,spfa)
所有数学公式均使用Latex并附有证明
作为网络流系列的第一张,我们从网络流的基础–最大流开始
2 什么是最大流
我们把网络流这个词拆开来看
网络,指的就是图,在网络流问题中大多为有向图,而且有边权(容量)
流,顾名思义,就是向水一样,从一个水源(源点)流出,流向终点(汇点)
那么会有多少水流向终点呢?这就是流量
这么说肯定不好理解,我们从图的最简单形态–链出发
上图片

可以把源点看做自来水厂,汇点看做你家,中间的点就是中转站,边就是水管,水管当然不是无限大的,肯定会有容量(即边权,图上用蓝色数字标出)
假设你需要 1 1 1个单位的水,自来水厂给你供水,每个水管中流的水量在下图中用绿色标注

显然,每个水管中的水量都小于容量,水管指定炸不了
流量为 1 1 1,即你最后收到了 1 1 1个单位的水
流量可以达到 1 1 1,没有任何一个水管炸掉,这就是可行流
但是这个没啥用…流量为 0 0 0还是可行流呢
我们考虑对于一个所有可行流的集合,求其中流量最大的一个
对于这张图,答案显然了,流量为2,在下图中用橙色标出
可以看到容量为 2 2 2的水管装满水了,不能再多了,这就是最大流
3最大流问题的求解
(1)问题转化–增广路的引入
假设图是一条链,答案肯定为容量最小边的容量,要不然就炸了
但是如果图不是一条链呢
我们把图变成链不就好了
在图上取一条路径,不就形成了链吗
我们还是举上面的例子,自来水厂给你家供水,肯定是选取一条路径将水送到你家,但是如果不够用,自来水厂就会再选取一条路径给你家供水
这就是增广路–一条连接源点和汇点,且流量未满的路径
假设现在已经找到了一条增广路,我们该怎么更新
根据上面的例子,自来水厂把水送到你家,肯定占用了水管,有的水管占满了,但是有的水管还有空间
我们求出增广路的流量–根据刚才链上问题的结论–就是这些边中容量的最小值
我们将路径上每一条边都减去这个占用的容量即可
这就构成了人们常说的残余网络
都减去容量了,我们只要用bfs求出原点到汇点的任意路径即可
如果不连通,算法就结束
这个算法好不好使,手动模拟就知道,请看图片

bfs求出一条增广路为 1 − > 2 − > 3 − > 4 1->2->3->4 1−>2−>3−>4
减去流量得下图

找不出增广路了,答案为 1 1 1
但是显然答案就不是 1 1 1,选取 1 − > 2 − > 4 1->2->4 1−>2−>4, 1 − > 3 − > 4 1->3->4 1−>3−>4两条增广路,答案显然为 2 2 2,我们的算法就这样炸了
(2)走回头路–EK算法
我们发现,无论怎么选取增广路,我们很难找出一个贪心策略一次得到最优解,选取不同增广路得到的结果不同
如果我们能走回头路多好
但是减去容量是算法成立的基础,我们不能动这一步
所以,我们要引入最大流的精髓–反向边
还是那个例子,自来水厂给你家送水,已经找到一条路径
但是,你说水不够,再给我送点,然而此时水管被占用了
那咋办,你直接把水送回去呗
所以,减去的容量,我们建反向边,把水送回去,就是走回头路
回归问题,我们求出 1 − > 2 − > 3 − > 4 1->2->3->4 1−>2−>3−>4的增广路中,图变成了这样
(反向边用红色标出)

这下,我们可以再次找到一条增广路 1 − > 3 − > 2 − > 4 1->3->2->4 1−>3−>2−>4
得到结果 2 2 2,AC了,那么,这个算法为什么是对的
其中涉及到了边 2 − > 3 2->3 2−>3,但是如果我们手动模拟,就会发现根本不能取这条边
我们发现 3 − > 2 3->2 3−>2这条反向边本来就代表着走回头路
但是正常 3 − > 4 3->4 3−>4这条边已经走过了,既然你替我走了,我就替你走,直接去走路径 2 − > 4 2->4 2−>4,这就相当于交换了位置,结果不改变
这就是EK算法,最大流问题的基础算法
附代码(c++)(问题参考洛谷P2740)
#include<bits/stdc++.h>
using namespace std;
int a[1145][1145],dist[114514],n,m,start,endd;
queue<int>q;
bool vis[114514];
bool bfs(int s,int t){memset(vis,false,sizeof(vis));memset(dist,-1,sizeof(dist));vis[s] = true;dist[s] = s;while(!q.empty()){q.pop();}q.push(s);while(!q.empty()){int x = q.front();q.pop();for(int i = 1;i<=n;i++){if(i!=x&&!vis[i]&&a[x][i]>0){q.push(i); dist[i] = x;vis[i] = true;if(i==t){return true; }}}}return false;
}
int EK(int s,int t){int ans = 0;while(bfs(s,t)){int mins = 0x3f3f3f3f;for(int i = t;i!=s;i = dist[i]){mins = min(mins,a[dist[i]][i]);}for(int i = t;i!=s;i = dist[i]){a[i][dist[i]]+=mins;a[dist[i]][i]-=mins;}ans+=mins;}return ans;
}
int main() {cin>>n>>m>>start>>endd;for(int i = 1;i<=m;i++){int u,v,w;cin>>u>>v>>w; a[u][v]+=w;}int ans = EK(start,endd);cout<<ans;return 0;
}
(3)EK的弊端
EK算法是正确的,但是时间复杂度非常玄学
比如说这种图

如果使用EK算法, 2 − > 3 2->3 2−>3那条边的方向会不断切换,这样下去将会找 200 200 200次增广路
直接TLE
EK算法可以优化,每次处理出最短路径,但是这也没有解决EK慢的问题,尤其是在更加稠密的图,EK的复杂度显然不够优秀
所以,为了解决这种问题,DINIC诞生了
(4)化图为树–DINIC算法
EK一次只能找一条增广路
其实就是把图变成链
如果我们想要从源点出发一次找多条增广路呢?
我们把图变成树,怎么变?
显然,用bfs分层,形成的就是一棵树了
然而进行一次bfs不一定正确
那进行多少次bfs呢?这个就很玄学
每次的残余网络的分层都不相同,所以,可以通过一直进行bfs知道搜索不出增广路
有了搜索树,这就可以引入dfs了,一次搜更多的增广路
不仅提高了稠密图中的时间复杂度,还防止了反复更新增广路的问题
这就是DINIC,在bfs建出的搜索树上跑dfs
代码中的体现就是储存每个点的层次
DINIC还有一个著名的当前弧优化
因为搜索树上有重复点,这些重复点连的边已经更新了的话,就不会再更新,所以我们标记一个 c u r cur cur数组即可
其实,在DINIC之外还有ISAP等算法
但是在信奥中有个不成文的规定,不能卡DINIC
新手可以先掌握DINIC
附代码(c++)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
ll nxt[114514],to[114514],w[114514];
ll idx = 1,head[114514];
ll n,m,s,t;
ll dep[114514],cur[114514];
ll ans = 0;
queue<ll> q;
void addedge(ll u,ll v,ll ww){idx++;nxt[idx] = head[u];to[idx] = v;w[idx] = ww;head[u] = idx;
}
bool bfs(){for(int i = 1;i<=n;i++){dep[i] = INF;}while(!q.empty()){q.pop();}q.push(s);dep[s] = 0;cur[s] = head[s];while(!q.empty()){ll x = q.front();q.pop();for(ll i = head[x];i!=0;i = nxt[i]){ll y = to[i];if(w[i]>0&&dep[y]==INF){q.push(y);cur[y] = head[y];dep[y] = dep[x]+1;if(y==t){return true;}}}}return false;
}
ll dfs(ll x,ll res){if(x==t){return res;}ll sum = 0,k,i;for(i = cur[x];i&&res;i = nxt[i]){cur[x] = i;ll y = to[i];if(w[i]>0&&(dep[y]==dep[x]+1)){k = dfs(y,min(res,w[i]));if(!k){dep[y] = INF;}w[i]-=k;w[i^1]+=k;sum+=k;res-=k;}}return sum;
}
void DINIC(){while(bfs()){ans+=dfs(s,INF);}
}
signed main(){cin>>n>>m>>s>>t;for(ll i = 1;i<=m;i++){ll u,v,ww;cin>>u>>v>>ww;addedge(u,v,ww);addedge(v,u,0);}DINIC();cout<<ans;return 0;
}
4后记
本蒟蒻的第一篇博客讲的是最短路,如今20篇过去了,这是第21篇
我这个大蒟蒻终于学会了网络流
本文作者是蒟蒻,如有错误请各位神犇指点
森林古猿出品,必属精品,请认准CSDN森林古猿1
相关文章:
论网络流(最大流篇)--新手入门超详解--包教包会
论网络流--新手入门超详解--包教包会 1 前言2 什么是最大流3最大流问题的求解(1)问题转化--增广路的引入(2)走回头路--EK算法(3)EK的弊端(4)化图为树--DINIC算法 4后记 1 前言 网络…...
环境搭建:全面详尽的 MongoDB Shell MongoDB Server介绍、安装、验证与配置指南(以 Windows 系统为主)
环境搭建:全面详尽的 MongoDB Shell & MongoDB Server介绍、安装、验证与配置指南(以 Windows 系统为主) MongoDB 是一个基于文档的 NoSQL 数据库,以其高性能、灵活性和可扩展性而受到广泛欢迎。本文将带您完成 MongoDB 的安装…...
使用 OpenSearch 的 K-NN 向量搜索来增强搜索功能
使用 OpenSearch 的 K-NN 向量搜索来增强搜索功能 许多应用程序都依赖于提供精确且相关的搜索结果的能力。尽管传统关系数据库的全文搜索功能在某些情况下已经足够,但这些数据库在从文本中提取语义含义或搜索结构化程度较低的数据方面可能会出现不足。在这篇博文中&…...
Less-2(闭合)
我们使用第一关的测试方法尝试一下,打咩 直接看源码,看到,尝试一下闭合 <?php ini_set("display_errors", 0); $str $_GET["keyword"]; echo "<h2 aligncenter>没有找到和".htmlspecialchars($str)."相…...
mysql介绍
MySQL是一种开源的关系型数据库管理系统(RDBMS),广泛用于存储和管理数据。它支持多种操作系统,如Linux、Windows、MacOS等。MySQL的特点包括: 1.开源免费:MySQL是开源的,可以免费使用和分发。 2…...
【ROS学习】ROS中 use_sim_time 参数的含义与作用
文章目录 写在前面一、背景描述二、 use_sim_time 参数的含义与作用三、举例说明1. 不设置use_sim_time (也即 use_sim_time false),播放数据集使用rosbag play **.bag 2. 不设置use_sim_time (也即 use_sim_time false),播放数据集使用rosbag play **…...
python-查找元素3(赛氪OJ)
[题目描述] 有n个不同的数,从小到大排成一列。现在告诉你其中的一个数x,x不一定是原先数列中的数。你需要输出最后一个<x的数在此数组中的下标。输入: 输入共两行第一行为两个整数n、x。第二行为n个整数,代表a[i]。输出&#x…...
苹果 Safari 的隐私保护与广告追踪问题 :技术进展与挑战
隐私保护的进展与挑战 近年来,浏览器行业在隐私保护技术方面取得了显著进展,尤其是在广告追踪领域。谷歌的 Chrome 浏览器推广了隐私沙盒,通过将用户可能感兴趣的主题分类并推送给广告商。Mozilla Firefox 和 Meta Facebook 则推出了一种名为…...
pytest之fixture
Pytest 中 Fixture 的 yield 用法 在软件测试中,设置和清理测试环境是一个重要的环节。Pytest 作为一个功能强大的测试框架,通过 Fixture 机制简化了这一过程。特别是yield语句的使用,使得 Fixture 能够在测试前进行设置,并在测试…...
Rancher
文章目录 Rancher1. 安装和配置2. 服务部署和管理3. 容器自动化缩容和扩容 Rancher Rancher 是一个开源的企业级容器管理平台,旨在简化容器化应用的部署、管理和运维。它支持多种容器编排引擎,如 Kubernetes、Docker Swarm 等,并提供了统一的…...
Wordpress建站问题记录
从一月到七月因为工作的情况没有进行太深入的开发,想着整理一下把做一个独立站把博客多个渠道发布一下,遇到几个问题在这里记录一下. 先写一下我的配置 系统: centos7 php: 7.4 wordpress: 6.6.1 mysql:8.0.6 1. HTTP 500 Internal 这个问题出现在我将wordpress的文件夹全部…...
JavaFx中通过线程池运行或者停止多个周期性任务
在JavaFX中,要实现点击按钮启动多个周期性任务并通过多线程执行,并在任务结束后将结果写入多个文本组件中,同时提供另一个按钮来停止这些任务,你可以使用ScheduledExecutorService来管理周期性任务,并使用AtomicBoolea…...
使用RabbitMQ实现异步支付状态通知
在支付系统中,如何确保支付状态的准确传递和处理显得尤为重要。今天,我们将以一个支付流程为例,探讨在引入RabbitMQ前后的实现和优化。 改造前 在引入RabbitMQ之前,我们通常会直接在支付方法中完成所有的操作。这包括查询支付单…...
[最短路dijkstra],启动!!!
总时间复杂度为 O ( ( n m ) log m ) P4779 【模板】单源最短路径(标准版) #include<bits/stdc.h> #define ll long long #define fi first #define se second #define pb push_back #define PII pair<int,int > #define I…...
Java企业微信服务商代开发获取AccessToken示例
这里主要针对的是企业微信服务商代开发模式 文档地址 可以看到里面大致有三种token,一个是服务商的token,一个是企业授权token,还有一个是应用的token 这里面主要有下面几个参数 首先是服务商的 corpid 和 provider_secret ,这个可…...
How does age change how you learn?(2)年龄如何影响学习能力?(二)
Do different people experience decline differently? 不同人经历的认知衰退会有不同吗? Do all people experience cognitive decline uniformly?Or do some people’s minds slip while others stay sharp much longer? 所有人经历的认知衰退都是一样的吗?还是有些人…...
可验证随机函数 vrf 概述
一、什么是VRF 背景: 在传统的区块链中,常用的随机算法是基于伪随机数生成器(Pseudorandom Number Generator,PRNG)的。PRNG是一种确定性算法,它根据一个初始种子生成一个看似随机的序列。在区块链中,通常使用的是伪随机数序列来选择区块的创建者、确定验证节点的轮换…...
鸿蒙双向绑定组件:TextArea、TextInput、Search、Checkbox,文本输入组件,图案解锁组件PatternLock
对象暂不支持双向绑定, 效果: 代码: Entry Component struct MvvmCase {StateisSelect: boolean falseStatesearchText: String ""StateinputText: string ""StateareaText: string ""build() {Grid() {G…...
JS 算法 - 计数器
theme: smartblue 题目描述 给定一个整型参数 n,请你编写并返回一个 counter 函数。这个 counter 函数最初返回 n,每次调用它时会返回前一个值加 1 的值 ( n , n 1 , n 2 ,等等)。 示例 1: 输入: n 10 ["cal…...
JavaScript基础——JavaScript运算符
赋值运算符 算术运算符 一元运算符 三元/三目运算符 比较运算符 逻辑运算符 运算符优先级 在JavaScript中,常见的运算符可以包括赋值运算符、一元运算符、算术运算符(二元运算符)、三元/三目运算符、比较运算符、逻辑运算符等࿰…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
