算法学习笔记:网络流#4——ISAP 求解最大流
算法学习笔记:网络流#4——ISAP 求解最大流
- 1. 前言
- 2. 模板
- 2.1 详解
- 2.2 正确性证明
- 2.3 代码
- 3. 算法对比
- 3.1 一般数据下的对比
- 3.2 特殊数据下的对比
- 4. 总结
1. 前言
本篇博文将会重点讲解 ISAP 求解最大流。
ISAP 求解最大流,是目前笔者知道的 除了 HLPP 之外的速度最快的最大流算法。
在学习 ISAP 求解最大流之前,您需要对以下知识有所了解,包括但不限于:网络流基础定义,FF/EK 求解最大流的 思路,dinic 求解最大流的 代码实现。
如果您对上述部分内容不熟悉,可以前往笔者所写的以下博文查看:
- 网络流基础定义:算法学习笔记:网络流#1——有关内容+算法导航
- FF/EK 求解最大流的 思路:算法学习笔记:网络流#2——EK 求解最大流
- dinic 求解最大流的 代码实现:算法学习笔记:网络流#3——dinic 求解最大流
2. 模板
模板题:P3376 【模板】网络最大流
2.1 详解
dinic 算法已经足够高效了,但是非常遗憾,dinic 仍然可以被卡掉,而且出现这种数据就代表 dinic 一定会 TLE,可以见最后面的数据比较。
不行,不稳定的算法我们不要,我们需要一种更加稳定的算法来处理最大流问题。
于是 ISAP 出现了。
ISAP 的核心思路就是:先用 一遍 BFS 从 ttt 开始 分层,同时记录 gapigap_igapi 数组表示当前层数为 iii 的点的数量,然后寻找增广路。
寻找增广路的过程中,一旦有一个点接受到的流量不能全部流出,那么就将这个点的层数提高 1,如果在提高完之后出现了断层(有一层没有点了),意味着算法结束。
大体分为 3 个步骤:
- 分层
- 找增广路以及提高层数
- 出现断层则结束算法
算法好理解,但是正确性呢?
2.2 正确性证明
ISAP 的正确性证明可能需要感性理解一下。
根据上面的算法步骤,当一个点推流推完的时候,这个点的层数不提高。
为什么?因为此时这个点的层数已经足够其推流了,不需要再提高,而且根据 dinic 算法的要求,流量只能在相邻层之间移动。
那么假设当前点推流推不出去了,这个时候需要提高层数,但是为什么这样就一定正确了呢?
想一个问题:如果当前的点无法推流了,那么前面的点呢?
是不是也无法推流了呀!那么因此如果我们提高了这个点的层数,前面的点也必须要提高层数(否则无法推流),此时就能够保证至少这个点接受流量不变。
而如果后面的点有层次更高的点,就可以继续推流,存在增广路;如果没有了,都是层次比较低的点,此时满足以下几个条件:
- 层次比较低的点推流完毕,无法再推流(即使得到 INFINFINF 的流量)。
- 由一,不存在增广路,满足算法结束条件。
- 于此同时,由于后面的点层次比较低,又推流完毕,没法提高层次,这个时候就会 出现断层,满足算法结束条件。
综上,算法正确性成立。
而 ISAP 巧妙的地方就在于它只要做一遍 BFS,大大减少运行时间,而且因为出现断层就是算法结束,可以保证不会被层次大的构造的图卡掉(具体见后面的对比)。
同样的,ISAP 可以使用当前弧优化,参照 dinic 的当前弧优化,证明也是一样的。
2.3 代码
代码:
/*
========= Plozia =========Author:PloziaProblem:P3376 【模板】网络最大流——ISAP 写法Date:2021/3/19
========= Plozia =========
*/#include <bits/stdc++.h>typedef long long LL;
const int MAXN = 200 + 10, MAXM = 5000 + 10;
const LL INF = 0x7f7f7f7f7f7f7f7f;
int n, m, s, t, dep[MAXN], gap[MAXN], cnt_Edge = 1, Head[MAXM << 1], cur[MAXN];
struct node {int to; LL val; int Next;} Edge[MAXM << 1];
int q[MAXN], l, r;
LL ans;int read()
{int sum = 0, fh = 1; char ch= getchar();for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);return (fh == 1) ? sum : -sum;
}
LL Min(LL fir, LL sec) {return (fir < sec) ? fir : sec;}
void add_Edge(int x, int y, int z) {Edge[++cnt_Edge] = (node){y, (LL)z, Head[x]}; Head[x] = cnt_Edge;}void bfs()
{q[l = r = 1] = t;memset(dep, -1, sizeof(dep)); dep[t] = 0; ++gap[0];//注意这地方 dep 初始化为 0 会出现一些奇奇怪怪的问题while (l <= r){int x = q[l++];for (int i = Head[x]; i; i = Edge[i].Next){int u = Edge[i].to;if (dep[u] != -1) continue ;dep[u] = dep[x] + 1; q[++r] = u; ++gap[dep[u]];}}
}LL dfs(int now, LL Flow)
{if (now == t) return Flow;LL used = 0;for (int i = cur[now]; i; i = Edge[i].Next){cur[now] = i; int u = Edge[i].to;if (Edge[i].val && dep[now] == dep[u] + 1)//注意控制层数{LL Minn = dfs(u, Min(Edge[i].val, Flow - used));if (Minn){Edge[i].val -= Minn; Edge[i ^ 1].val += Minn; used += Minn;if (used == Flow) return used;}}}--gap[dep[now]];//提高层数if (gap[dep[now]] == 0) dep[s] = n + 1;//出现断层++dep[now]; ++gap[dep[now]];return used;
}int main()
{n = read(), m = read(), s = read(), t = read();for (int i = 1; i <= m; ++i){int x = read(), y = read(), z = read();add_Edge(x, y, z); add_Edge(y, x, 0);}bfs();while (dep[s] <= n) {for (int i = 1; i <= n; ++i) cur[i] = Head[i]; ans += dfs(s, INF);}//出现断层就结束算法printf("%lld\n", ans);return 0;
}
3. 算法对比
3.1 一般数据下的对比
一般数据的对比参照 luogu 的模板题提交结果。
- 全部采用快读
- 全部采用 STL 容器的队列(所以上面的 ISAP 代码不适用于这里的对比)
- 全部不开启 O2O_2O2 优化
- dinic 和 ISAP 采用当前弧优化
- 全部不对重边进行合并处理,这样做是为了更具有可比性
- 代码长度以 Dev-C++ 数据为准
- 因为码风问题,或许笔者所测的代码长度与读者所测的代码长度差距很大,请读者谅解。
- 因为 FF 被群殴了所以没有 FF
算法 | 代码长度 | 使用时间 | 使用空间 |
---|---|---|---|
EK | 1.746K | 605ms | 892.00KB |
dinic | 2.071K | 45ms | 884.00KB |
ISAP | 2.078K | 38ms | 900.00KB |
从上面的表格可以看出来:
- EK 的码量相对短一点,但是耗时太长了。
- dinic 和 ISAP 其实在这些数据下没什么差别。
其实一般的网络流题目主要考建模能力,一般不会来卡你算法时间(当然 EK 被卡掉比较普遍),但是万一出题人构造数据卡你,那么还是使用 ISAP 吧。
顺便提一下,根据目前笔者所了解,ISAP 貌似不支持费用流问题,dinic 支持,所以 dinic 还是很重要的。
当然 ISAP 实在是太难卡了(目前笔者无法构造 hack 数据)以至于没必要学 HLPP,HLPP 码量又长又容易写错,而且必须加各种玄学优化才会比 ISAP 快,普通的 HLPP 跑不过 ISAP。
3.2 特殊数据下的对比
此处的数据引用了洛谷用户 @离散小波变换° 在加强版模板 P4722 【模板】最大流 加强版 / 预留推进 题解里面给出的数据,在此表示感谢。
P.S. 笔者感觉表格里面 n,mn,mn,m 的数据是假的,因为好像 dinic 在这种数据下根本没法在一秒里面跑完,真正的数据或许是 n=104,m=3×104n=10^4,m=3 \times 10^4n=104,m=3×104。
首先给出原文中的数据表格:
测试数据 | 性质 1 | 性质 2 | 性质 3 | 性质 4 | 性质 5 | n=n=n= | m=m=m= |
---|---|---|---|---|---|---|---|
1 | √ | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||
2 | √ | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||
3 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
4 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
5 | √ | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||
6 | √ | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||
7 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
8 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
9 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
10 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
11 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
12 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
13 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
14 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
15 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
16 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
17 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
18 | √ | √ | 10510^5105 | 3×1053 \times 10^53×105 | |||
19 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
20 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
21 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
22 | √ | 10510^5105 | 3×1053 \times 10^53×105 | ||||
23 | 10510^5105 | 3×1053 \times 10^53×105 | |||||
24 | 10510^5105 | 3×1053 \times 10^53×105 |
笔者备注:方便起见,原文中所有的 × 已经使用空格代替,23,24 两组数据表示数据不是特殊构造,不具有 5 种性质。
- 性质 1:不会出现环
- 性质 2:层次数量很少
- 性质 3:层次数量很大
- 性质 4:无解
- 性质 5:答案较小
笔者备注:在原文中,性质一是专门针对 FF 所设计的,也就是说 FF 只要有环就会 TLE,且没环的数据仍然消耗了极高的时间。
评测环境说明:
- 时间限制为 60s60s60s。
- 开启快读,O2O_2O2 优化。
- dinic 和 ISAP 使用当前弧优化。
- 使用 lemon 评测机测试
最后结果如下:
测试数据 | EK | dinic | ISAP |
---|---|---|---|
1 | 0.171s0.171s0.171s | 0.625s0.625s0.625s | 0.265s0.265s0.265s |
2 | 0.156s0.156s0.156s | 0.562s0.562s0.562s | 0.265s0.265s0.265s |
3 | 0.625s0.625s0.625s | 0.828s0.828s0.828s | 0.390s0.390s0.390s |
4 | 0.312s0.312s0.312s | 0.578s0.578s0.578s | 0.328s0.328s0.328s |
5 | 0.046s0.046s0.046s | 2.468s\red{2.468s}2.468s | 0.218s0.218s0.218s |
6 | 0.078s0.078s0.078s | 5.546s\red{5.546s}5.546s | 0.203s0.203s0.203s |
7 | 0.109s0.109s0.109s | 5.216s\red{5.216s}5.216s | 0.328s0.328s0.328s |
8 | 0.218s0.218s0.218s | 7.812s\red{7.812s}7.812s | 0.265s0.265s0.265s |
9 | 0.375s0.375s0.375s | 1.281s\purple{1.281s}1.281s | 0.375s0.375s0.375s |
10 | 0.156s0.156s0.156s | 0.781s0.781s0.781s | 0.187s0.187s0.187s |
11 | 0.046s0.046s0.046s | 0.312s0.312s0.312s | 0.203s0.203s0.203s |
12 | 2.703s\red{2.703s}2.703s | 0.875s0.875s0.875s | 0.328s0.328s0.328s |
13 | 0.156s0.156s0.156s | 0.703s0.703s0.703s | 0.203s0.203s0.203s |
14 | 0.328s0.328s0.328s | 0.500s0.500s0.500s | 0.218s0.218s0.218s |
15 | 0.171s0.171s0.171s | 0.296s0.296s0.296s | 0.296s0.296s0.296s |
16 | 0.234s0.234s0.234s | 0.562s0.562s0.562s | 0.296s0.296s0.296s |
17 | 0.140s0.140s0.140s | 4.687s\red{4.687s}4.687s | 0.343s0.343s0.343s |
18 | 0.031s0.031s0.031s | 2.921s\red{2.921s}2.921s | 0.296s0.296s0.296s |
19 | 0.040s0.040s0.040s | 2.359s\red{2.359s}2.359s | 0.312s0.312s0.312s |
20 | 0.078s0.078s0.078s | 4.656s\red{4.656s}4.656s | 0.390s0.390s0.390s |
21 | 0.312s0.312s0.312s | 0.500s0.500s0.500s | 0.218s0.218s0.218s |
22 | 0.203s0.203s0.203s | 1.000s\purple{1.000s}1.000s | 0.234s0.234s0.234s |
23 | 0.062s0.062s0.062s | 0.343s0.343s0.343s | 0.265s0.265s0.265s |
24 | 0.281s0.281s0.281s | 1.015s\purple{1.015s}1.015s | 0.328s0.328s0.328s |
总用时 | 7.027s7.027s7.027s | 46.428s46.428s46.428s | 6.754s6.754s6.754s |
根据上表,笔者总结如下:
- dinic 在层次很深的时候会被卡掉。
- EK 玄学吊打全场,但是被 #12 卡掉了。
- ISAP 非常稳。
4. 总结
ISAP 的主要思路:一遍 BFS 分层,然后利用断层巧妙寻找增广路。
到目前为止,笔者已经讲完了 EK,dinic,ISAP 求解最大流,接下来将会进入网络流的另外一个分支:费用流。
传送门:
- 算法学习笔记:网络流#5——EK 求解费用流
- 算法学习笔记:网络流#6——dinic 求解费用流
算法学习笔记:网络流#4——ISAP 求解最大流相关推荐
- 算法学习笔记 网络流之最大流算法
文章目录 26.1 流网络 1. 流网络和流 2. 流的一个例子 3. 使用反平行边来建模问题 4. 具有多个源点和多个汇点的网络 26.2 *Ford-Fulkerson* 方法 1. 残存网络 2 ...
- 一次递减代码matlab,DEA算法学习系列之三:一次性求解CCR模型所有DMU参数——效率、规模效益、有效性特征、调整值的matlab代码...
<DEA算法学习系列之三:一次性求解CCR模型所有DMU参数--效率.规模效益.有效性特征.调整值的matlab代码>由会员分享,可在线阅读,更多相关<DEA算法学习系列之三:一次性 ...
- 网络流算法学习笔记——最大流问题基本概念和Ford-Fulkerson方法(标号法C++实现)
屈婉玲<算法设计与分析>第2版第7章网络流算法学习笔记. 基本概念 最大流问题,相当于有从s到t的供水系统,每段路径都有限定流量,除了s.t两地外,每个中间点都不能滞留,从s流入多少,就从 ...
- Python最优化算法学习笔记(Gurobi)
微信公众号:数学建模与人工智能 github地址:https://github.com/QInzhengk/Math-Model-and-Machine-Learning Python最优化算法学习笔 ...
- matlab中x从0到5不含0,关于MATLAB的数学建模算法学习笔记
关于MATLAB的数学建模算法学习笔记 目录 线性规划中应用: (3) 非线性规划: (3) 指派问题;投资问题:(0-1问题) (3) 1)应用fmincon命令语句 (3) 2)应用指令函数:bi ...
- 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配
数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...
- 局部线性嵌入LLE算法--学习笔记
流形学习是一大类基于流形的框架,形象说明流形降维: ISOMAP(更多细节可参考:isomap降维算法--学习笔记_Wsyoneself的博客-CSDN博客): LLE: 和传统的PCA,LDA等关注 ...
- 基于MVS的三维重建算法学习笔记(四)— 立体匹配经典算法Semi-Global Matching(SGM)论文翻译及要点解读
基于MVS的三维重建算法学习笔记(四)- 立体匹配经典算法Semi-Global Matching(SGM)论文翻译及要点解读 声明 SGM概述 Cost Calculation(像素代价计算)--M ...
- 白鲸优化算法学习笔记
白鲸优化算法学习笔记 1. 引言 白鲸优化算法(Whale Optimization Algorithm,简称WOA)是一种基于自然界中的白鲸行为而发展起来的启发式优化算法.该算法模拟了白鲸群体的寻食 ...
最新文章
- 互联网大脑如何产生“梦境“并形成元宇宙
- 系统linux/redhat6.5 zabbix 2.47监控nginx1.8.0 (下)
- putty遇到ctrl+s
- Android中怎样使用createTempFile实现将字节数据创建到临时文件并转换成FileOutputStream和FileInputStream
- 《C++代码设计与重用》——1.7 参考文献和相关资料
- Spring MVC中使用 Swagger2 构建Restful API
- 力扣—— 79/212. 单词搜索
- 什么是低信噪比图像及处理方法
- 编程十年 (6):虚荣的C
- CSS3魔法堂:说说Multi-column Layout
- SQLite在指定列后面插入字段_如何用SQL语句添加和修改字段?
- 见山只是山 见水只是水——提升对继承的认识
- 苹果6p计算机在哪里设置方法,苹果手机怎么设置铃声【图文教程,不用电脑,1分钟完成】...
- unity android 触屏,Unity 移动端触摸屏操作
- Win10系统台式机如何调节系统亮度
- [附源码]Nodejs计算机毕业设计基于java学科竞赛管理系统Express(程序+LW)
- linux下TC+HTB流量控制
- ubuntu使用docker-compose安装rabbitmq并实现延迟交换机
- 尊贤、谦虚、谨慎、交友、有恒、微渐、慎始终、因果
- 投影仪服务器注册商标属于哪类,投影仪商标注册第几类?附:好听的投影仪商标名字...