http://www.renfei.org/blog/isap.html
算法与数学
网络流-最大流问题 ISAP 算法解释2013-08-07Renfei Song  2 条评论
内容提要 [隐藏]
1 约定
2 引入
3 算法解释
4 实现
ISAP 是图论求最大流的算法之一,它很好的平衡了运行时间和程序复杂度之间的关系,因此非常常用。约定我们使用邻接表来表示图,表示方法可以见文章带权最短路 Dijkstra, SPFA, Bellman-Ford, ASP, Floyd-Warshall 算法分析或二分图的最大匹配、完美匹配和匈牙利算法的开头(就不重复贴代码了)。在下文中,图的源点(source)表示为  s  ,汇点(sink)表示为  t  ,当前节点为  u  。建图时,需要建立双向边(设反向的边容量为0)才能保证算法正确。引入求解最大流问题的一个比较容易想到的方法就是,每次在残量网络(residual network)中任意寻找一条从  s  到  t  的路径,然后增广,直到不存在这样的路径为止。这就是一般增广路算法(labeling algorithm)。可以证明这种不加改进的贪婪算法是正确的。假设最大流是  f  ,那么它的运行时间为  O( f⋅∣E∣) 。但是,这个运行时间并不好,因为它和最大流  f  有关。人们发现,如果每次都沿着残量网络中的最短增广路增广,则运行时间可以减为  O(∣E∣2⋅∣V∣)  。这就是最短增广路算法。而 ISAP 算法则是最短增广路算法的一个改进。其实,ISAP 的意思正是「改进的最短增广路」 (Improved Shortest Augmenting Path)。顺便说一句,上面讨论的所有算法根本上都属于增广路方法(Ford-Fulkerson method)。和它对应的就是大名鼎鼎的预流推进方法(Preflow-push method)。其中最高标号预流推进算法(Highest-label preflow-push algorithm)的复杂度可以达到  O(∣V∣2∣E∣−−−√) 。虽然在复杂度上比增广路方法进步很多,但是预流推进算法复杂度的上界是比较紧的,因此有时差距并不会很大。算法解释概括地说,ISAP 算法就是不停地找最短增广路,找到之后增广;如果遇到死路就 retreat,直到发现  s, t  不连通,算法结束。找最短路本质上就是无权最短路径问题,因此采用 BFS 的思想。具体来说,使用一个数组  d  ,记录每个节点到汇点  t  的最短距离。搜索的时候,只沿着满足  d[u]=d[v]+1  的边  u→v  (这样的边称为允许弧)走。显然,这样走出来的一定是最短路。原图存在两种子图,一个是残量网络,一个是允许弧组成的图。残量网络保证可增广,允许弧保证最短路(时间界较优)。所以,在寻找增广路的过程中,一直是在残量网络中沿着允许弧寻找。因此,允许弧应该是属于残量网络的,而非原图的。换句话说,我们沿着允许弧,走的是残量网络(而非原图)中的最短路径。当我们找到沿着残量网络找到一条增广路,增广后,残量网络肯定会变化(至少少了一条边),因此决定允许弧的  d  数组要进行相应的更新(顺便提一句,Dinic 的做法就是每次增广都重新计算  d  数组)。然而,ISAP 「改进」的地方之一就是,其实没有必要马上更新  d  数组。这是因为,去掉一条边只可能令路径变得更长,而如果增广之前的残量网络存在另一条最短路,并且在增广后的残量网络中仍存在,那么这条路径毫无疑问是最短的。所以,ISAP 的做法是继续增广,直到遇到死路,才执行 retreat 操作。说到这里,大家应该都猜到了,retreat 操作的主要任务就是更新  d  数组。那么怎么更新呢?非常简单:假设是从节点  u  找遍了邻接边也没找到允许弧的;再设一变量  m  ,令  m  等于残量网络中  u  的所有邻接点的  d  数组的最小值,然后令  d[u]  等于  m+1  即可。这是因为,进入 retreat 环节说明残量网络中  u  和  t  已经不能通过(已过时)的允许弧相连,那么  u  和  t  实际上在残量网络中的最短路的长是多少呢?(这正是  d  的定义!)显然是残量网络中  u  的所有邻接点和  t  的距离加  1  的最小情况。特殊情况是,残量网络中  u  根本没有邻接点。如果是这样,只需要把  d[u]  设为一个比较大的数即可,这会导致任何点到  u  的边被排除到残量网络以外。(严格来说只要大于等于  ∣V∣  即可。由于最短路一定是无环的,因此任意路径长最大是  ∣V∣−1  )。修改之后,只需要把正在研究的节点  u  沿着刚才走的路退一步,然后继续搜索即可。讲到这里,ISAP 算法的框架内容就讲完了。对于代码本身,还有几个优化和实现的技巧需要说明。算法执行之前需要用 BFS 初始化  d  数组,方法是从  t  到  s  逆向进行。
算法主体需要维护一个「当前节点」  u  ,执行这个节点的前进、retreat 等操作。
记录路径的方法非常简单,声明一个数组  p  ,令  p[i]  等于增广路上到达节点  i  的边的序号(这样就可以找到从哪个顶点到的顶点  i  )。需要路径的时候反向追踪一下就可以了。
判断残量网络中  s, t  不连通的条件,就是  d[s]≥∣V∣ 。这是因为当  s, t  不连通时,最终残量网络中  s  将没有任何邻接点,对  s  的 retreat 将导致上面条件的成立。
GAP 优化。GAP 优化可以提前结束程序,很多时候提速非常明显(高达 100 倍以上)。GAP 优化是说,进入 retreat 环节后,  u, t  之间的连通性消失,但如果  u  是最后一个和  t  距离  d[u]  (更新前)的点,说明此时  s, t  也不连通了。这是因为,虽然  u, t  已经不连通,但毕竟我们走的是最短路,其他点此时到  t  的距离一定大于  d[u]  (更新前),因此其他点要到  t  ,必然要经过一个和  t  距离为  d[u]  (更新前)的点。GAP 优化的实现非常简单,用一个数组记录并在适当的时候判断、跳出循环就可以了。
另一个优化,就是用一个数组保存一个点已经尝试过了哪个邻接边。寻找增广的过程实际上类似于一个 BFS 过程,因此之前处理过的邻接边是不需要重新处理的(残量网络中的边只会越来越少)。具体实现方法直接看代码就可以,非常容易理解。需要注意的一点是,下次应该从上次处理到的邻接边继续处理,而非从上次处理到的邻接边的下一条开始。
最后说一下增广过程。增广过程非常简单,寻找增广路成功(当前节点处理到  t  )后,沿着你记录的路径走一遍,记录一路上的最小残量,然后从  s  到  t  更新流量即可
int source;         // 源点
int sink;           // 汇点
int p[max_nodes];   // 可增广路上的上一条弧的编号
int num[max_nodes]; // 和 t 的最短距离等于 i 的节点数量
int cur[max_nodes]; // 当前弧下标
int d[max_nodes];   // 残量网络中节点 i 到汇点 t 的最短距离
bool visited[max_nodes];// 预处理, 反向 BFS 构造 d 数组
bool bfs()
{memset(visited, 0, sizeof(visited));queue<int> Q;Q.push(sink);visited[sink] = 1;d[sink] = 0;while (!Q.empty()) {int u = Q.front();Q.pop();for (iterator_t ix = G[u].begin(); ix != G[u].end(); ++ix) {Edge &e = edges[(*ix)^1];if (!visited[e.from] && e.capacity > e.flow) {visited[e.from] = true;d[e.from] = d[u] + 1;Q.push(e.from);}}}return visited[source];
}// 增广
int augment()
{int u = sink, df = __inf;// 从汇点到源点通过 p 追踪增广路径, df 为一路上最小的残量while (u != source) {Edge &e = edges[p[u]];df = min(df, e.capacity - e.flow);u = edges[p[u]].from;}u = sink;// 从汇点到源点更新流量while (u != source) {edges[p[u]].flow += df;edges[p[u]^1].flow -= df;u = edges[p[u]].from;}return df;
}int max_flow()
{int flow = 0;bfs();memset(num, 0, sizeof(num));for (int i = 0; i < num_nodes; i++) num[d[i]]++;int u = source;memset(cur, 0, sizeof(cur));while (d[source] < num_nodes) {if (u == sink) {flow += augment();u = source;}bool advanced = false;for (int i = cur[u]; i < G[u].size(); i++) { Edge& e = edges[G[u][i]];if (e.capacity > e.flow && d[u] == d[e.to] + 1) {advanced = true;p[e.to] = G[u][i];cur[u] = i;u = e.to;break;}}if (!advanced) { // retreatint m = num_nodes - 1;for (iterator_t ix = G[u].begin(); ix != G[u].end(); ++ix)if (edges[*ix].capacity > edges[*ix].flow)m = min(m, d[edges[*ix].to]);if (--num[d[u]] == 0) break; // gap 优化num[d[u] = m+1]++;cur[u] = 0;if (u != source)u = edges[p[u]].from;}}return flow;
}

ISAP 算法的学习相关推荐

  1. 最大流ISAP算法模板

    这两天学习了最大流,下面是ISAP算法模板: const int inf = 0x3fffffff; template <int N, int M> struct Isap {int to ...

  2. 最短增广路Isap算法 网络流

    最短增广路 请先理解 bfs的求增广路的算法,再来学习Isap算法 最短增广路Isap算法 图片来源 <趣学算法>人民邮电出版社 陈小玉 /* 最短可增广路:重贴标签算法Isap 算法设计 ...

  3. 网络流FF,EK,dinicm, isap算法板子//带注释

    FF算法 #include <iostream> #include <cstdio> #include <stack> #include <sstream&g ...

  4. 计算机视觉算法——Transformer学习笔记

    算机视觉算法--Transformer学习笔记 计算机视觉算法--Transformer学习笔记 1. Vision Transformer 1.1 网络结构 1.2 关键知识点 1.2.1 Self ...

  5. matlab中存档算法代码,MATLAB 智能算法超级学习手册中程序代码

    [实例简介] MATLAB 智能算法超级学习手册中程序代码 [实例截图] [核心代码] dc90ef43-7920-434e-bdb8-0636c31c0b44 └── MATLAB 智能算法超级学习 ...

  6. KMP算法的学习经验

    KMP算法的学习经验 (欢迎指正错误, 欢迎喷) 什么是kmp(完) kmp的额外知识(完) 暴力匹配的缺点,和代码实现(完) next[]数组的预先知识,了解前后缀,相同前后缀.(完) kmp的关键 ...

  7. 【网络流】最大流问题(EK算法带模板,Dinic算法带模板及弧优化,ISAP算法带模板及弧优化)上下界网络流

    本blog重点是代码 网络流的相关概念 流网络(flow network) 流(flow) 网络的流 残留网络(residual network) 增广路径(augmenting path) Edmo ...

  8. 数据结构与算法深入学习_我最喜欢的免费课程,用于深入学习数据结构和算法...

    数据结构与算法深入学习 by javinpaul 由javinpaul Data structures and algorithms are some of the most essential to ...

  9. leetcode 刷500道题,笔试/面试稳吗?谈谈算法的学习

    来源公众号:苦逼的码农 作者:帅地 想要学习算法.应付笔试或者应付面试手撕算法题,相信大部分人都会去刷 Leetcode,有读者问?如果我在 leetcode 坚持刷它个 500 道题,以后笔试/面试 ...

最新文章

  1. 第三部分:Android 应用程序接口指南---第一节:应用程序组件---第七章 App Widgets...
  2. NSAssert的使用
  3. 基于SignalR的消息推送与二维码描登录实现
  4. UI组件之AdapterView及其子类(三)Spinner控件详解
  5. 推荐一款万能抓包神器:Fiddler Everywhere
  6. [转]解决IE下CSS背景图片闪烁的Bug
  7. 开发可扩展的Web API
  8. python多线程并发每秒6000_Python多线程并发的误区
  9. python实现arxiv论文数据解析处理
  10. css设置自适应屏幕高度
  11. 毕设不要慌,论文写作法
  12. 电商商家如何利用商品信息制定价格策略?
  13. 非常时期的囤货手册,建议查看收藏
  14. 盲盒抽奖微信小程序源码
  15. cad快速选择命令快捷键_CAD快捷键命令:倒角的使用技巧
  16. 网络核心交换机和普通交换机有什么区别?
  17. 模拟信号隔离器|隔离放大器|隔离变送器 常见问题解答
  18. Bit Twiddling Hacks
  19. 数据结构学习,哈希表(链地址)
  20. 为什么苹果文件连接服务器没反应,苹果手机连接电脑没反应,教您苹果手机连接电脑没反应具体解决方法...

热门文章

  1. Python 语句跳出循环
  2. Linux系统运行时参数命令--Linux基础命令和工具
  3. dSPACE Mid-Size Simulator Introduction
  4. 席马云(微博)昨日在杭州和黄章会面
  5. 耳穴诊疗记录0526-内人舌头苔厚微黄
  6. 3600万中国人在抖音“上清华”
  7. 利用setuptools发布Python程序到PyPI,为Python添砖加瓦
  8. ROS使用IDE Eclipse
  9. idea配置检查XML中的SQL语法及书写sql语句智能提示
  10. 小清新蓝天白云Mac高清壁纸5K