基本没有严谨证明。

Part. 1 概念

Part. 1-1 流网络

流网络是一个有向图(不考虑反向边),我们把这个图记为 \(G=(V,E)\)。

其中有两个特殊的点 \(s,t\),分别成为源点和汇点。

对于每一个 \((u,v)\in E\),我们给它两个属性,一个 \(c(u,v)\),表示这条边的容量;一个 \(f(u,v)\),表示这条边的流量。

每一条边都需要满足两个性质:

  • 容量限制,\(0\le f(u,v)\le c(u,v)\)。
  • 流量守恒,\(\sum_{(x,u)\in E}f(x,u)=\sum_{u,y\in E}f(u,y)\),即流进来多少流出去多少。

(这里的定义是 yxc / 算导 的定义,和网络上大部分的资料不同)

我们称一个网络的可行流的流量为 \(\left(\sum_{(s,x)\in E}f(s,x)\right)-\left(\sum_{(y,s)\in E}f(y,s)\right)\),即流出源点的流量减去流入源点的流量。

一个可行流的方案我们记为 \(f\),其流量记为 \(|f|\)。

Part. 1-2 残量网络

残量网络即把原网络的反向边算进去,把反向边的边集记为 \(E'\),把残量网络表示为 \(G_{f}=(V,E\cup E')\),把残量网络的一个可行流方案记为 \(f'\),同理 \(|f'|\),\(c_{f}(u,v)\),\(f'(u,v)\)。

这里定义 \(f\) 之间的加法:

首先我们知道对于 \(G\) 的每一个不同的 \(f\) 而言,其 \(G'\) 是不同的。

我们定义 \(c'(u,v)=\begin{cases}c(u,v)-f(u,v),(u,v)\in E \\ \displaystyle f(v,u),(u,v)\in E'\end{cases}\)。

那么 \(f\) 之间的加法是这样定义的:\(f'(u,v)=f'(u,v)+f(u,v),f'(v,u)=f'(v,u)-f(u,v)\)。(定义不太清楚,自行意会吧)

然后可以证明 \(|f+f'|=|f|+|f'|\),注意 \(|f'|\) 可能为负。

Part. 1-3 增广路

增广路就是一条从 \(s\) 到 \(t\) 的简单路径,满足路上每条边的容量都 \(>0\)。

Part. 1-4 割

割是对 \(V\) 进行的划分。

首先我们划分出两个集合 \(S,T\),需满足 \(s\in S,t\in T\) 且 \(S\cup T=V,S\cap T=\empty\)。

我们再定义割的容量:\(c(S,T)=\sum_{u\in S}\sum_{v\in T}c(u,v)\)(不考虑反向边)。

割的流量:\(f(S,T)=\left(\sum_{u\in S}\sum_{v\in T}f(u,v)\right)-\left(\sum_{u\in S}\sum_{v\in T}f(v,u)\right)\)。

这里流量和容量的定义不对称,不过 不 影 响。

可以证明对于任意可行流的任意割,有 \(f(S,T)\le c(S,T)\),也有 \(|f|=f(S,T)\le c(S,T)\)。

最小割指容量最小的割。

Part. 1-5 最大流最小割定理

  • 可行流 \(f\) 为最大流
  • 对于可行流 \(f\),其 \(G_{f}\) 不存在增广路。
  • 存在割 \([S,T]\),\(|f|=c(S,T)\)。

以上三条可以互相推出。

最大流等于最小割。

Part. 2 算法

Part. 1-1 Edmond-Karp

不会

Part. 1-2 Dinic

先讲讲暴力 FF。

算了懒得讲了自己看洛谷题解的暴力代码吧。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10010, E = 200010; int n, m, s, t;
LL first[N];
LL to[E], nxt[E], val[E]/*残余容量*/;
int cnt = 1;
//cnt初值1,第一条边的标号为2(二进制10),第二条是3(二进制11)
//有啥好处呢?
//我们加入一条边时,紧接着加入它的反向边(初始容量0)
//这两条边的标号就是二进制最后一位不相同,一个0、一个1
//所以要召唤 p 这条边的反向边,只需用 p ^ 1
//如果cnt初值为0,就做不到。当然初值-1也可以,略需改动 //关于图中真正的反向边,可能引起顾虑,应该让它们标号相邻?
//其实不用。该找到的增广路都会找到的 bool vis[N];//限制增广路不要重复走点,否则很容易爆栈
//兜一大圈走到汇点,还不如直接走到汇点 void addE(int u, int v, LL w) {
++cnt;
to[cnt] = v;
val[cnt] = w;
nxt[cnt] = first[u];
first[u] = cnt;
}
LL dfs(int u, LL flow) {
//注意,在走到汇点之前,无法得知这次的流量到底有多少
if (u == t)
return flow;//走到汇点才return一个实实在在的流量 vis[u] = true;
for (int p = first[u]; p; p = nxt[p]) {
int v = to[p];
if (val[p] == 0 or vis[v])//无残量,走了也没用
continue;
int res = 0;
if ((res = dfs(v, min(flow, val[p]))) > 0) {
//↑顺着流过去,要受一路上最小容量的限制
val[p] -= res;//此边残余容量减小
val[p ^ 1] += res;//以后可以顺着反向边收回这些容量,前提是对方有人了
return res;
}
}
return 0;//我与终点根本不连通(依照残量网络),上一个点不要信任我
}
int main() {
scanf("%d %d %d %d", &n, &m, &s, &t);
for (int i = 1; i <= m; ++i) {
int u, v; LL w;
scanf("%d %d %lld", &u, &v, &w);
addE(u, v, w);
addE(v, u, 0);//和正向边标号相邻
//反向边开始容量为0,表示不允许平白无故走反向边
//只有正向边流量过来以后,才提供返还流量的机会
}
LL res = 0, tot = 0;
while (memset(vis, 0, sizeof(vis)) and (res = dfs(s, 1e18/*水库无限*/)) > 0)
tot += res;//进行若干回合的增广 printf("%lld\n", tot);
return 0;
}

接下来讲讲如何 Dinic。

其实我不理解 Dinic 比暴力优在哪里

/* okay | there's been */

#include <cstdio>

namespace mySpace {
typedef long long LL; const int MAXN = 200 + 5, MAXM = 5000 + 5; int rint () {
int x = 0, f = 1; char c = getchar ();
for ( ; c < '0' || c > '9'; c = getchar () ) f = c == '-' ? -1 : f;
for ( ; c >= '0' && c <= '9'; c = getchar () ) x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
return x * f;
} template<typename _T> _T MIN ( const _T x, const _T y ) { return x < y ? x : y; } struct GraphSet {
int to, nx;
LL wt;
GraphSet () : to ( 0 ), nx ( 0 ), wt ( 0 ) {}
GraphSet ( const int a, const int b, const LL c ) : to ( a ), nx ( b ), wt ( c ) {}
} as[MAXM * 2]; int n, m, s, t, bgn[MAXN], cnt = 1, lav[MAXN], ali[MAXN]; void makeEdge ( const int u, const int v, const LL w ) { as[++ cnt] = GraphSet ( v, bgn[u], w ), bgn[u] = cnt; } bool bfs () {
for ( int i = 1; i <= n; ++ i ) lav[i] = 0;
int nowl = 1, nowr = 1;
ali[1] = s, lav[s] = 1;
for ( ; nowl <= nowr; ) {
int u = ali[nowl ++];
for ( int i = bgn[u]; i; i = as[i].nx ) {
int v = as[i].to; LL w = as[i].wt;
if ( ! w || lav[v] ) continue;
lav[v] = lav[u] + 1, ali[++ nowr] = v;
}
}
return lav[t];
} LL dfs ( const int u, LL in ) {
if ( u == t ) return in;
LL out = 0;
for ( int i = bgn[u]; i; i = as[i].nx ) {
if ( ! in ) break;
int v = as[i].to; LL w = as[i].wt;
if ( ! w || lav[v] != lav[u] + 1 ) continue;
LL ret = dfs ( v, MIN ( in, w ) );
as[i].wt -= ret, as[i ^ 1].wt += ret;
in -= ret, out += ret;
}
if ( ! out ) lav[u] = 0;
return out;
} LL calcMXflow () {
LL res = 0;
for ( ; bfs (); res += dfs ( s, 1e18 ) ) ;
return res;
} void main () {
n = rint (), m = rint (), s = rint (), t = rint ();
for ( int i = 1; i <= m; ++ i ) {
int u = rint (), v = rint (); LL w = rint ();
makeEdge ( u, v, w ), makeEdge ( v, u, 0 );
}
printf ( "%lld\n", calcMXflow () );
}
} int main () {
mySpace :: main ();
return 0;
}

Part. 3 无源汇上下界可行流

Part. 3-1 概念

就是每条边的容量从变成了 \([cl(u,v),cr(u,v)]\)。

Part. 3-2 xxxx

可以想见通过把容量转化为 \([0,cr(u,v)-cl(uv)]\),但这样容量不一定能守恒。

记每个点的入流量为 \(in_{u}\),出流为 \(out_{u}\)。

  • \(in_{u}\ge out_{u}\)

从 \(S\) 向 \(u\) 连一条容量 \(in_{u}-out_{u}\) 的边。

  • \(in_{u}<out_{u}\)

从 \(u\) 向 \(T\) 连一条容量 \(out_{u}-in_{u}\) 的边。

Part. 4 有源汇上下界最大流

Part. 4-1 概念

有规定的源点汇点了。

Part. 4-2 xxxx

图都是从 这里 偷的。这个博客几百年没过图了

有源汇后,\(s\) 和 \(t\) 不满足流量守恒了,于是从 \(s\) 到 \(t\) 连一条容量 \([0,\infty]\) 的边即可。

然后就可以把 \(s\) 和 \(t\) 当成普通点处理,重现建立超源超汇,用无源汇上下界可行流的方法跑一遍,看是不是一个可行流,如果是就从 \(s\) 到 \(t\) 跑一遍最大流。

Part. 5 有源汇上下界最小流

Part. 5-1 概念

你猜。

Part. 5-2 xxxx

首先加上 \((t,s)\) 的边跑最大流。

设 \(d\) 是加上的边的流量,删掉加上的边。

答案是 \(d-\text{maxflow}(t,s)\)。

这里 copy 的。

Part. 6 多源汇最大流

Part. 6-1 概念

你猜。

Part. 6-2 xxxx

没啥区别,建超源超汇即可。

Note -「网络流 flows」的更多相关文章

  1. Note -「Lagrange 插值」学习笔记

    目录 问题引入 思考 Lagrange 插值法 插值过程 代码实现 实际应用 「洛谷 P4781」「模板」拉格朗日插值 「洛谷 P4463」calc 题意简述 数据规模 Solution Step 1 ...

  2. Note -「动态 DP」学习笔记

    目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...

  3. Note -「单位根反演」学习笔记

    \(\mathcal{Preface}\)   单位根反演,顾名思义就是用单位根变换一类式子的形式.有关单位根的基本概念可见我的这篇博客. \(\mathcal{Formula}\)   单位根反演的 ...

  4. Note -「Mobius 反演」光速入门

    目录 Preface 数论函数 积性函数 Dirichlet 卷积 Dirichlet 卷积中的特殊函数 Mobius 函数 & Mobius 反演 Mobius 函数 Mobius 反演 基 ...

  5. Note -「Min_25 筛」“你就说这素因子你要不要吧?你要不要?”

      赛上想写,Track Lost 了属于是. \(\mathscr{Intro}\)   Min_25 筛是用于求积性函数前缀和,同时顺带求出一些"有意思"的信息的筛法.   一 ...

  6. Template -「网络流 & 二分图」

    EK. 很少用到,知道思想即可. 懒得写封装的屑. queue<int> q; int Cap[MAXN][MAXN], Flow[MAXN][MAXN], Aug[MAXN], fa[M ...

  7. LOJ6003 - 「网络流 24 题」魔术球

    原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...

  8. LOJ6002 - 「网络流 24 题」最小路径覆盖

    原题链接 Description 求一个DAG的最小路径覆盖,并输出一种方案. Solution 模板题啦~ Code //「网络流 24 题」最小路径覆盖 #include <cstdio&g ...

  9. LOJ6001 - 「网络流 24 题」太空飞行计划

    原题链接 Description 有个实验和个仪器,做实验有报酬买仪器有花费.每个实验都需要一些仪器,求最大净收益(实验报酬仪器花费),并输出一组方案. Solution 实验向所需仪器连边,实验的点 ...

  10. LOJ6000 - 「网络流 24 题」搭配飞行员

    原题链接 题意简述 求二分图的最大匹配. 题解 这里写的是匈牙利算法. 表示节点的当前匹配. 为真表示在这一轮匹配中,无法给节点一个新的匹配.所以如果为真就不用再dfs它了,直接continue就好. ...

随机推荐

  1. 深度学习应用篇-计算机视觉-图像分类[2]:LeNet、AlexNet、VGG、GoogleNet、DarkNet模型结构、实现、模型特点详细介绍

    深度学习应用篇-计算机视觉-图像分类[2]:LeNet.AlexNet.VGG.GoogleNet.DarkNet模型结构.实现.模型特点详细介绍 1.LeNet(1998) LeNet是最早的卷积神 ...

  2. CANoe学习笔记(二):创建第一个事件触发帧(基于LIN)

    内容: 创建一个事件触发帧: 包含几个不同无条件帧: 事件触发帧的触发: 事先准备: 创建三个文件夹,用来放不同类型文件: 工程创建 新建一个Lin工程,双击即可,然后命名为LINconf保存. 创建 ...

  3. 什么是Sparse by default for crates.io

    当 Rust crate 发布到 crates.io 上时,可以启用"Sparse by default"特性,这意味着默认情况下,crate 不会包含所有依赖项在上传到 crat ...

  4. CKS 考试题整理 (10)-Dockerfile检测

    Task 分析和编辑给定的Dockerfile /cks/docker/Dockerfile(基于ubuntu:16.04 镜像), 并修复在文件中拥有的突出的安全/最佳实践问题的两个指令. 分析和编 ...

  5. navicate的安装使用

    1 navicat概述 Navicat for MySQL 是管理和开发 MySQL 或 MariaDB 的理想解决方案. 这套全面的前端工具为数据库管理.开发和维护提供了一款直观而强大的图形界面. ...

  6. Rainbond助力“信创应用”迁移上云

    Rainbond v5.14.2 版本,又称信创版本.从这个版本开始,开源用户也可以利用 Rainbond 管理符合信创要求的硬件计算资源.在这个版本中,产品团队将此前只在企业版产品中存在的信创相关功 ...

  7. Dapr 发布模糊测试报告|Dapr 完成模糊测试审核

    Dapr 团队最近在博客上发布了 Dapr 完成模糊测试审核[1]的文章,该审计是 CNCF 通过模糊测试改善[2]开源云原生项目安全状况的计划的一部分.该审计由 Ada Logics[3] 于 20 ...

  8. 【项目学习】ERC-4337 抽象账户项目审计过程中需要注意的安全问题

    抽象账户是什么 抽象账户(也有叫合约钱包)是 EIP-4337 提案提出的一个标准.简单来说就是通过智能合约来实现一个"账户(account)",在合约中自行实现签名验证的逻辑.这 ...

  9. CF371D Vessels题解

    思路: 定义一个权值并查集,权值保存这个集合还可以存下多少水. 如果这个集合可以存放的水已经小于要装入的水,就将这个集合与下一个集合合并. 否则,直接把这个集合可以存放的水减去要装入的水的体积. 代码 ...

  10. Web网页音视频通话之Webrtc相关操作(一)

    目录 打开摄像头/关闭摄像头 静音/解除静音 打开视频/关闭视频 截图且下载 打开摄像头/关闭摄像头 效果图 HTML <!DOCTYPE html> <html lang=&quo ...