网络流学习笔记

网络流基础

基本概念

  • 源点(source) s s s,汇点 t t t

  • 容量:约等于边权。不存在的边流量可视为 0 0 0 ( u , v ) (u,v) (u,v) 的流量通常记为 c ( u , v ) c(u,v) c(u,v)(capacity)。

  • 流(flow):每条边上的流不能超过它的容量,注意这个限制是所有经过路径的边共享的。除了源点和汇点,其他所有点流入的流量都等于流出的流量。通常用 f f f 表示。

  • 割:把结点分成两部分 { S , T } \{S,T\} {S,T},且满足 s ∈ S , t ∈ T s\in S,t\in T sS,tT { S , T } \{S,T\} {S,T} 是图的一个 s s s- t t t 割, s s s- t t t { S , T } \{S,T\} {S,T} 的容量为 ∑ u ∈ S ∑ v ∈ T c ( u , v ) \sum\limits_{u\in S}\sum\limits_{v\in T}c(u,v) uSvTc(u,v)

  • 残留网络:有源点、汇点,且每条边都有残留容量的网络。

  • 增广路:从残留网络的源点到汇点的路径。对于增广路,给每一条边都加上等量流量,此过程称为增广。

常见问题

  • 最大流问题:给定每条边的流量,求得尽可能大的流量。
  • 最小割问题:给定每条边的流量,求一个容量尽可能大的 s s s- t t t { S , T } \{S,T\} {S,T}
  • 最小费用最大流问题:给定每条边的流量和权值(费用),求对于所有可能的最大流,费用最小的一个。
  • 上下界网络流问题:给定每条边的流量上界和下界,求一种可行的流使得满足限制。

最大流问题

以下代码均为 P3376 【模板】网络最大流 代码。

先介绍一种思想——Ford–Fulkerson 增广(FF 增广)。即不断在残留网络中找一条增广路,向汇点发送可能的最大流量,得到新的残留网络,不断寻找增广路,直到没有增广路为止。此时有最大流。

在 FF 增广的过程中,为了保证正确性,我们要引入反向边。对于每一条边 ( u , v ) (u,v) (u,v),建一条 c ( v , u ) = 0 c(v,u)=0 c(v,u)=0 的反向边。反向边其实相当于一种撤回操作,因此在增广的过程中,给正向边减去流量的同时要给反向边加上流量。

反向边的“抵消”操作使得在错误的增广路选择顺序下也可以得到正确答案。

FF 增广的时间复杂度为 O ( E f max ⁡ ) O(Ef_{\max}) O(Efmax)

EK 算法

通过 BFS 实现的 FF 增广过程。最坏时间复杂度为 O ( V E 2 ) O(VE^2) O(VE2),一般可以处理 1 0 4 10^4 104 规模的网络。

注意这里的链前 cnt 初始值要设定为 1 1 1,方便通过异或操作查找反向边(这样可以使第一条边的编号为偶数, 2 n ⊕ 1 = 2 n + 1 , ( 2 n + 1 ) ⊕ 1 = 2 n 2n \oplus 1=2n+1,(2n+1)\oplus 1=2n 2n1=2n+1,(2n+1)1=2n)。用 pre 数组记录当前的增广路,flow 数组记录当前增广路上的流量。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;const ll maxn=5005;
int n,m,s,t,cnt=1,head[maxn],pre[maxn];
ll flow[maxn]/*记录到x的最大可行流*/,ans;
struct edge{int to,nxt;ll c;}e[maxn*2];
bool vis[maxn],flag[205][205];void add(int x,int y,ll z){e[++cnt]={y,head[x],z},head[x]=cnt;}bool bfs()
{for(int i=1;i<=n;i++) vis[i]=0;queue<int> q;vis[s]=1,q.push(s),flow[s]=LLONG_MAX;while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];i;i=e[i].nxt){if(!e[i].c) continue;if(vis[e[i].to]) continue;flow[e[i].to]=min(flow[x],e[i].c),pre[e[i].to]=i,q.push(e[i].to),vis[e[i].to]=1;if(e[i].to==t) return 1;}}return 0;
}void ek()
{int x=t;while(x!=s) e[pre[x]].c-=flow[t],e[pre[x]^1].c+=flow[t],x=e[pre[x]^1].to;ans+=flow[t];
}int main()
{cin>>n>>m>>s>>t;for(int i=1,u,v,w;i<=m;i++) cin>>u>>v>>w,add(u,v,w),add(v,u,0);while(bfs()) ek();cout<<ans;return 0;
}

Dinic 算法

先通过 BFS,把图根据结点到源点的距离分层,只按照层数递增的方向增广。注意每次增广后都要重新将图分层。

为了保证 Dinic 算法的时间复杂度正确性,我们需要引入当前弧优化。如果一条边 ( u , v ) (u,v) (u,v) 的容量已经用完,或 v v v 的后侧已经增广至阻塞,则 u u u 的流量无需流向出边 ( u , v ) (u,v) (u,v)。对于每个结点,维护它的出边中第一个需要尝试流出的出边。维护的这个指针称为当前弧。由于我们的边是顺次遍历的,所以当遍历到第 i i i 条边时,前面的边一定已经不能继续流,直接修改新的当前弧 now[x]=i

还可以用多路增广的方法优化时间复杂度。在某点找到一条增广路后,如果还有剩余流量,继续从该点寻找增广路。

DFS 过程中,对于当前结点 x x x,它可以分给后面结点最多 f max ⁡ f_{\max} fmax 流量;对于当前访问的边 ( u , v ) (u,v) (u,v),分配的流量是最大流量与已经用的流量之差与边的容量取 min ⁡ \min min 的结果。

最坏时间复杂度为 O ( V 2 E ) O(V^2E) O(V2E)

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;const int maxn=5005;
int n,m,s,t,cnt=1,head[maxn],now[maxn];
ll flow[maxn],ans,dis[205];
struct edge{int to,nxt;ll c;}e[maxn*2];void add(int x,int y,ll z){e[++cnt]={y,head[x],z},head[x]=cnt;}bool bfs()
{for(int i=1;i<=n;i++) dis[i]=LLONG_MAX;queue<int> q;q.push(s);dis[s]=0,now[s]=head[s];while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];i;i=e[i].nxt)if(e[i].c>0&&dis[e[i].to]==LLONG_MAX){q.push(e[i].to),now[e[i].to]=head[e[i].to],dis[e[i].to]=dis[x]+1;if(e[i].to==t) return 1;}}return 0;
}int dfs(int x,ll mxf)//mxf是能给后面点分配的最大流量
{if(x==t) return mxf;ll sum=0;//sum是从x点实际分配出的流量for(int i=now[x];i;i=e[i].nxt){now[x]=i;if(e[i].c>0&&dis[e[i].to]==dis[x]+1){int ff=dfs(e[i].to,min(mxf-sum,e[i].c));e[i].c-=ff,e[i^1].c+=ff,sum+=ff;if(ff==mxf) return ff;}}return sum;
}signed main()
{cin>>n>>m>>s>>t;for(int i=1,u,v,w;i<=m;i++) cin>>u>>v>>w,add(u,v,w),add(v,u,0);while(bfs()) ans+=dfs(s,LLONG_MAX);cout<<ans;return 0;
}

最小费用最大流问题

( u , v ) (u,v) (u,v) 流量为 f ( u , v ) f(u,v) f(u,v) 时,花费的费用为 f ( u , v ) × w ( u , v ) f(u,v)\times w(u,v) f(u,v)×w(u,v),要求在最大化 ∑ ( u , v ) ∈ E f ( u , v ) \sum\limits_{(u,v)\in E}f(u,v) (u,v)Ef(u,v) 的情况下最小化 ∑ ( u , v ) ∈ E f ( u , v ) × w ( u , v ) \sum\limits_{(u,v)\in E} f(u,v)\times w(u,v) (u,v)Ef(u,v)×w(u,v),该问题即最小费用最大流问题。

SSP 算法

SSP(Successive Shortest Path)算法,思想是每次寻找费用最小的增广路进行增广,直到图上不存在增广路为止。

注意图中不能存在单位费用为负的圈。

具体实现就是把 EK/Dinic 算法中 BFS 找增广路的过程用 SPFA 代替,同时反向边的花费为负。时间复杂度 O ( V E f max ⁡ ) O(VEf_{\max}) O(VEfmax)

以下是基于 EK 算法的实现:

#include <bits/stdc++.h>
using namespace std;const int maxn=5005,maxm=1e5+5;
struct edge{int to,nxt,c,w;}e[maxm];
int head[maxn],pre[maxn],dis[maxn],cnt=1,n,m,s,t,mxf,minc,flow[maxn];
bool vis[maxn];void add(int x,int y,int z,int q){e[++cnt]=(edge){y,head[x],z,q},head[x]=cnt;}bool spfa()
{queue<int> q;for(int i=1;i<=n;i++) dis[i]=INT_MAX,vis[i]=0;q.push(s),dis[s]=0,flow[s]=INT_MAX,vis[s]=1,pre[t]=-1;while(!q.empty()){int x=q.front();q.pop(),vis[x]=0;for(int i=head[x];i;i=e[i].nxt)if(e[i].c&&dis[e[i].to]>dis[x]+e[i].w){dis[e[i].to]=dis[x]+e[i].w,pre[e[i].to]=i,flow[e[i].to]=min(flow[x],e[i].c);if(!vis[e[i].to]) q.push(e[i].to),vis[e[i].to]=1;}}return pre[t]!=-1;
}void ek()
{while(spfa()){int x=t;mxf+=flow[t],minc+=flow[t]*dis[t];while(x!=s) e[pre[x]].c-=flow[t],e[pre[x]^1].c+=flow[t],x=e[pre[x]^1].to;// cout<<flow[t]<<' '<<dis[t]<<endl;}
}int main()
{cin>>n>>m>>s>>t;for(int i=1,u,v,w,c;i<=m;i++) cin>>u>>v>>w>>c,add(u,v,w,c),add(v,u,0,-c);ek();cout<<mxf<<' '<<minc;return 0;
}

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

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

相关文章

解决MySQL大版本升级导致.Net(C#)程序连接报错问题

数据库版本从MySQL 5.7.21 升级到 MySQL8.0.21 数据升级完成后&#xff0c;直接修改程序的数据库连接配置信息 <connectionStrings> <add name"myConnectionString" connectionString"server192.168.31.200;uidapp;pwdFgTDkn0q!75;databasemail;&q…

【语义分割】语义分割概念及算法介绍

文章目录 一、基本概念二、研究现状2.1 传统算法2.2 深度学习方法 三、数据集及评价指标3.1 常用数据集3.2 常用指标 四、经典模型参考资料 一、基本概念 语义分割是计算机视觉中很重要的一个方向。不同于目标检测和识别&#xff0c;语义分割实现了图像像素级的分类。它能够将…

使用langchain-chatchat里,faiss库中报错: AssertionError ,位置:assert d == self.d

发生报错&#xff1a; AssertionError&#xff0c;发生位置&#xff1a;class_wrappers.py里 assert d self.d&#xff0c;假如输出语句&#xff0c;查看到是因为d和self.d维度不匹配造成&#xff0c;解决方式&#xff1a; 删除langchain-chatchat/knowledge_base里的info.db…

【iOS免越狱】利用IOS自动化web-driver-agent_appium-实现自动点击+滑动屏幕

1.目标 在做饭、锻炼等无法腾出双手的场景中&#xff0c;想刷刷抖音 刷抖音的时候有太多的广告 如何解决痛点 抖音自动播放下一个视频 iOS系统高版本无法 越狱 安装插件 2.操作环境 MAC一台&#xff0c;安装 Xcode iPhone一台&#xff0c;16 系统以上最佳 3.流程 下载最…

Python 算法高级篇:堆排序的优化与应用

Python 算法高级篇&#xff1a;堆排序的优化与应用 引言 1. 什么是堆&#xff1f;2. 堆的性质3. 堆排序的基本原理4. 堆排序的 Python 实现5. 堆排序的性能和优化6. 堆排序的实际应用7. 总结 引言 堆排序是一种高效的排序算法&#xff0c;它基于数据结构中的堆这一概念。堆排序…

C++进阶语法——OOP(面向对象)【学习笔记(四)】

文章目录 1、C OOP⾯向对象开发1.1 类&#xff08;classes&#xff09;和对象&#xff08;objects&#xff09;1.2 public、private、protected访问权限1.3 实现成员⽅法1.4 构造函数&#xff08;constructor&#xff09;和 析构函数&#xff08;destructor&#xff09;1.4.1 构…

Java基础 多线程

1.多线程创建方式1&#xff0c;继承Thread类&#xff1a; 2.多线程创建方式2&#xff1a; 匿名内部类写法 package thread;public class ThreadTest {public static void main(String[] args) {Runnable runnable new Runnable() {Overridepublic void run() {for (int i 0…

笔记本电脑的摄像头找不到黑屏解决办法

这种问题一般来说就是缺少驱动&#xff0c;就要下载驱动。 问题&#xff1a; 解决办法&#xff1a; 1.进入联想官网下载驱动 网站&#xff1a;https://newsupport.lenovo.com.cn/driveDownloads_index.html?v9d9bc7ad5023ef3c3d5e3cf386e2f187 2.下载主机编号检测工具 3.下…

虚幻中的网络概述一

前置&#xff1a;在学习完turbo强大佬的多人fps之后发觉自己在虚幻网络方面还有许多基础知识不太清楚&#xff0c;结合安宁Ken大佬与虚幻官方文档进行补足。 补充&#xff1a;官方文档中的描述挺好的&#xff0c;自己只算是搬运和将两者结合加强理解。 学习虚幻中的网络先从虚…

【Docker】Python Flask + Redis 练习

一、构建flask镜像 1.准备文件 创建app.py,内容如下 from flask import Flask from redis import Redis app Flask(__name__) redis Redis(hostos.environ.get(REDIS_HOST,127.0.0.1),port6379)app.route(/) def hello():redis.incr(hits)return f"Hello Container W…

串行原理编程,中文编程工具中的串行构件,串行连接操作简单

串行通信原理编程&#xff0c;中文编程工具中的串行通信构件&#xff0c;串行通信连接设置简单 编程系统化课程总目录及明细&#xff0c;点击进入了解详情。https://blog.csdn.net/qq_29129627/article/details/134073098?spm1001.2014.3001.5502 串行端口 是串行的基础&#…

【C++】类与对象 第二篇(构造函数,析构函数,拷贝构造,赋值重载)

目录 类的6个默认成员函数 初始化和清理 1.构造函数 2.析构函数 3.共同点 拷贝复制 1.拷贝构造 使用细节 2.赋值重载 运算符重载 < < > > ! 连续赋值 C入门 第一篇(C关键字&#xff0c; 命名空间&#xff0c;C输入&输出)-CSDN博客 C入门 第二篇( 引…

【开源】基于SpringBoot的海南旅游景点推荐系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

2017年上半年上午易错题(软件设计师考试)

CPU 执行算术运算或者逻辑运算时&#xff0c;常将源操作数和结果暂存在&#xff08; &#xff09;中。 A &#xff0e; 程序计数器 (PC) B. 累加器 (AC) C. 指令寄存器 (IR) D. 地址寄存器 (AR) 某系统由下图所示的冗余部件构成。若每个部件的千小时可靠度都为 R &…

如何使用手机蓝牙设备作为电脑的解锁工具像动态锁那样,蓝牙接近了电脑,电脑自动解锁无需输入开机密码

环境&#xff1a; Win10 专业版 远程解锁 蓝牙解锁小程序 问题描述&#xff1a; 如何使用手机蓝牙设备作为电脑的解锁工具像动态锁那样&#xff0c;蓝牙接近了电脑&#xff0c;电脑自动解锁无需输入开机密码 手机不需要拿出来&#xff0c;在口袋里就可以自动解锁&#xff…

C#,数值计算——分类与推理,基座向量机的 Svmgenkernel的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public abstract class Svmgenkernel { public int m { get; set; } public int kcalls { get; set; } public double[,] ker { get; set; } public double[] y { get; set…

机器学习-特征选择:如何使用互信息特征选择挑选出最佳特征?

一、引言 特征选择在机器学习中扮演着至关重要的角色&#xff0c;它可以帮助我们从大量的特征中挑选出对目标变量具有最大预测能力的特征。互信息特征选择是一种常用的特征选择方法&#xff0c;它通过计算特征与目标变量之间的互信息来评估特征的重要性。 互信息是信息论中的一…

Csdn文章编写参考案例

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

cosover是什么?crossover23又是什么软件

cosover是篮球里的过人技巧。 1.crossover在篮球中的本意是交叉步和急速交叉步。crossover 是篮球术语&#xff0c;有胯下运球、双手交替运球&#xff0c;交叉步过人、急速大幅度变向等之意。 2.在NBA里是指包括胯下运球、变向、插花在内的过人的技巧。 NBA有很多著名的Cross…

Linux进程等待

一、进程等待是什么&#xff1f; 通过系统调用wait/waitpid&#xff0c;来对子进程进行状态检验与回收的工作。 二、为什么要有进程等待 1、子进程退出&#xff0c;父进程如果不管不顾&#xff0c;就可能造成‘僵尸进程’的问题&#xff0c;进而造成内存泄漏。 另外&#xf…