算法学习笔记:网络流#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. 网络流基础定义:算法学习笔记:网络流#1——有关内容+算法导航
  2. FF/EK 求解最大流的 思路:算法学习笔记:网络流#2——EK 求解最大流
  3. dinic 求解最大流的 代码实现:算法学习笔记:网络流#3——dinic 求解最大流

2. 模板

模板题:P3376 【模板】网络最大流

2.1 详解

dinic 算法已经足够高效了,但是非常遗憾,dinic 仍然可以被卡掉,而且出现这种数据就代表 dinic 一定会 TLE,可以见最后面的数据比较。

不行,不稳定的算法我们不要,我们需要一种更加稳定的算法来处理最大流问题。

于是 ISAP 出现了。

ISAP 的核心思路就是:先用 一遍 BFS 从 ttt 开始 分层,同时记录 gapigap_igapi​ 数组表示当前层数为 iii 的点的数量,然后寻找增广路。

寻找增广路的过程中,一旦有一个点接受到的流量不能全部流出,那么就将这个点的层数提高 1,如果在提高完之后出现了断层(有一层没有点了),意味着算法结束。

大体分为 3 个步骤:

  1. 分层
  2. 找增广路以及提高层数
  3. 出现断层则结束算法

算法好理解,但是正确性呢?

2.2 正确性证明

ISAP 的正确性证明可能需要感性理解一下。

根据上面的算法步骤,当一个点推流推完的时候,这个点的层数不提高。

为什么?因为此时这个点的层数已经足够其推流了,不需要再提高,而且根据 dinic 算法的要求,流量只能在相邻层之间移动

那么假设当前点推流推不出去了,这个时候需要提高层数,但是为什么这样就一定正确了呢?

想一个问题:如果当前的点无法推流了,那么前面的点呢?

是不是也无法推流了呀!那么因此如果我们提高了这个点的层数,前面的点也必须要提高层数(否则无法推流),此时就能够保证至少这个点接受流量不变。

而如果后面的点有层次更高的点,就可以继续推流,存在增广路;如果没有了,都是层次比较低的点,此时满足以下几个条件:

  1. 层次比较低的点推流完毕,无法再推流(即使得到 INFINFINF 的流量)。
  2. 由一,不存在增广路,满足算法结束条件。
  3. 于此同时,由于后面的点层次比较低,又推流完毕,没法提高层次,这个时候就会 出现断层,满足算法结束条件。

综上,算法正确性成立。

而 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 求解最大流相关推荐

  1. 算法学习笔记 网络流之最大流算法

    文章目录 26.1 流网络 1. 流网络和流 2. 流的一个例子 3. 使用反平行边来建模问题 4. 具有多个源点和多个汇点的网络 26.2 *Ford-Fulkerson* 方法 1. 残存网络 2 ...

  2. 一次递减代码matlab,DEA算法学习系列之三:一次性求解CCR模型所有DMU参数——效率、规模效益、有效性特征、调整值的matlab代码...

    <DEA算法学习系列之三:一次性求解CCR模型所有DMU参数--效率.规模效益.有效性特征.调整值的matlab代码>由会员分享,可在线阅读,更多相关<DEA算法学习系列之三:一次性 ...

  3. 网络流算法学习笔记——最大流问题基本概念和Ford-Fulkerson方法(标号法C++实现)

    屈婉玲<算法设计与分析>第2版第7章网络流算法学习笔记. 基本概念 最大流问题,相当于有从s到t的供水系统,每段路径都有限定流量,除了s.t两地外,每个中间点都不能滞留,从s流入多少,就从 ...

  4. Python最优化算法学习笔记(Gurobi)

    微信公众号:数学建模与人工智能 github地址:https://github.com/QInzhengk/Math-Model-and-Machine-Learning Python最优化算法学习笔 ...

  5. matlab中x从0到5不含0,关于MATLAB的数学建模算法学习笔记

    关于MATLAB的数学建模算法学习笔记 目录 线性规划中应用: (3) 非线性规划: (3) 指派问题;投资问题:(0-1问题) (3) 1)应用fmincon命令语句 (3) 2)应用指令函数:bi ...

  6. 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配

    数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...

  7. 局部线性嵌入LLE算法--学习笔记

    流形学习是一大类基于流形的框架,形象说明流形降维: ISOMAP(更多细节可参考:isomap降维算法--学习笔记_Wsyoneself的博客-CSDN博客): LLE: 和传统的PCA,LDA等关注 ...

  8. 基于MVS的三维重建算法学习笔记(四)— 立体匹配经典算法Semi-Global Matching(SGM)论文翻译及要点解读

    基于MVS的三维重建算法学习笔记(四)- 立体匹配经典算法Semi-Global Matching(SGM)论文翻译及要点解读 声明 SGM概述 Cost Calculation(像素代价计算)--M ...

  9. 白鲸优化算法学习笔记

    白鲸优化算法学习笔记 1. 引言 白鲸优化算法(Whale Optimization Algorithm,简称WOA)是一种基于自然界中的白鲸行为而发展起来的启发式优化算法.该算法模拟了白鲸群体的寻食 ...

最新文章

  1. 互联网大脑如何产生“梦境“并形成元宇宙
  2. 系统linux/redhat6.5 zabbix 2.47监控nginx1.8.0 (下)
  3. putty遇到ctrl+s
  4. Android中怎样使用createTempFile实现将字节数据创建到临时文件并转换成FileOutputStream和FileInputStream
  5. 《C++代码设计与重用》——1.7 参考文献和相关资料
  6. Spring MVC中使用 Swagger2 构建Restful API
  7. 力扣—— 79/212. 单词搜索
  8. 什么是低信噪比图像及处理方法
  9. 编程十年 (6):虚荣的C
  10. CSS3魔法堂:说说Multi-column Layout
  11. SQLite在指定列后面插入字段_如何用SQL语句添加和修改字段?
  12. 见山只是山 见水只是水——提升对继承的认识
  13. 苹果6p计算机在哪里设置方法,苹果手机怎么设置铃声【图文教程,不用电脑,1分钟完成】...
  14. unity android 触屏,Unity 移动端触摸屏操作
  15. Win10系统台式机如何调节系统亮度
  16. [附源码]Nodejs计算机毕业设计基于java学科竞赛管理系统Express(程序+LW)
  17. linux下TC+HTB流量控制
  18. ubuntu使用docker-compose安装rabbitmq并实现延迟交换机
  19. 尊贤、谦虚、谨慎、交友、有恒、微渐、慎始终、因果
  20. 投影仪服务器注册商标属于哪类,投影仪商标注册第几类?附:好听的投影仪商标名字...

热门文章

  1. 2018年中国规模以上工业企业利润增长10.3%
  2. C# 中的SqlCommand 的用法和它的几个方法
  3. STM32上可用的的SM 2 3 4国密算法
  4. macbook忘记root用户密码,如何重置密码
  5. Qt实战案例(41)——利用QWinTaskbarButton和QWinTaskbarProgress类实现任务栏进度条的显示
  6. QNAP NAS固件更新失败的手工处理
  7. C++和qt designer的混合编程--动态计算加法小工具
  8. Prometheus入门教程
  9. 库函数strcpy函数的实现
  10. 编译chm格式PHP手册