前言

看到网上Dinic和ISAP的比较,多数人认为ISAP更快,不容易爆栈。当然,也有少数人认为,在多数情况下,Dinic比较稳定。我认为Dinic的思路比ISAP更简明,所以选择了Dinic算法

UPD20190626:突然发现这篇博客阅读量1000多了,把我吓得不轻,感谢各位网友的支持。

介绍

Dinic算法本身,自然是解决最大流(普通最大流,最大流最小割)的算法。通过处理,也可以解决二分图的最大匹配(下文介绍),最大权闭合图。

算法介绍:介绍Dinic之前,我们先介绍一下最大流。在最大流的题目中,图被称为"网络",每条边的边权被称作"流量",有一个起点(源点)和一个终点(汇点)。我们要求的最大流,可以这样形象地理解:源点有一个水库,里面有无限吨水(QWQ),汇点也有一个水  库,希望得到最多的水。我们假设每个河道一天只能输水n吨(及网络流中的流量),求解汇点最多能的到几吨水。再给一个正式的定义:最大流是指网络中满足弧流量限制条件和平衡条件且具有最大流量的可行流

下面我们正式介绍Dinic:

首先引出网络流算法中的链,给个正式定义:链是网络中的一个顶点序列,这个序列中前后两个顶点有弧相连(其实我认为这个定义无关紧要,所以重点看下面弧的定义)。

弧 :弧分为两种,第一种是前向弧是指方向和链一致的弧(简单的说就是输入的边)---前向弧,第二种弧是指方向和链不一致的弧(简单的说就是输入的边反一反)---后向弧。

好了接下来要引出一个网络流算法的重要概念

增广路

给个正式的定义:

1、增广路是一条链

2、链上的前向弧都是非饱和弧

链上的后向弧都是非零弧

3、链是由源点到汇点的

总结一下:额...这听起来好像啥都没说(滑稽)

谈谈我的理解:

增广路是一个边集,是一条从源点到汇点的路径

增广路有一个权值表示该增广路的最大流量,而最大流量的大小是边集中流量最小的边的流量。

剩余网络

由反向弧组成的网络,关于反向弧的权的问题,后文会介绍。

说了一大堆,下面正式介绍Dinic算法

Dinic算法的大致步骤

1、建立网络(包括正向弧和反向弧(初始边权为0)),将总流量置为0

2、构造层次网络(怎么又有新概念 T_T)

简单的说,就是求出每个点u的层次,u的层次是从源点到该点的最短路径(注意:这个最短路是指弧的权都为1的情况下的最短路),若与源点不连通,层次置为-1

一遍BFS轻松解决

3、判断汇点的层次是否为-1

是:再见,算法结束,输出当前的总流量

否:下一步

4、用一次DFS完成所有增广,增广是什么呢?

增广(我的理解):通过DFS找上述的增广路,找到了之后,将每条边的权都减去该增广路中拥有最小流量的边的流量,将每条边的反向边的权增加这个值,同时将总流量加上这个值

DFS直到找不到一条可行的从原点到汇点的路

5、goto 步骤2

细节处理,如何快速找到一条边的反向边:边的编号从0开始,反向边加在正向边之后,反向边即为该点的编号异或1

复杂度:理论上来说,最慢应该是O((n^2)*m),n表点数,m表边数,实际上呢,应该快得不少

代码实例:(参见洛谷P3376)

传送门[>洛谷<]   重要提示:您的等级必须达到蓝色以上,否则后果自负

当前弧优化

在DFS的时候记录当前已经计算到第几条边了,避免重复计算。

那么具体原理是什么呢?

每当我们发现一条新的增广路时,由于算法"贪心"的性质,该增广路上的可用流量其实已经被支配完全(即使放入另一条增广路也毫无意义)。

那么当我们每次到达一个节点时,必定有许多边已经被完全支配,又由于我们深度优先搜索的性质,故可以记录上一次搜索到哪一条边,下一次直接从该边开始DFS。

在下一次构建层次网络时注意将head数组还原

代码

* 使用当前弧优化

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <queue>
  4. #include <algorithm>
  5.  
  6. using namespace std;
  7.  
  8. const int MAX = (1ll << 31) - 1;
  9.  
  10. int read(){
  11. int x = 0; int zf = 1; char ch = ' ';
  12. while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
  13. if (ch == '-') zf = -1, ch = getchar();
  14. while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
  15. }
  16.  
  17. struct Edge{
  18. int to;
  19. int dis;
  20. int next;
  21. } edges[210000];
  22.  
  23. int cur[10010], head[10010], edge_num = -1;
  24. int n, m, s, t;
  25.  
  26. void addEdge2(int from, int to, int dis){
  27. edges[++edge_num].to = to;
  28. edges[edge_num].dis = dis;
  29. edges[edge_num].next = head[from];
  30. head[from] = edge_num;
  31. }
  32.  
  33. void addEdge(int from, int to, int dis){
  34. addEdge2(from, to, dis), addEdge2(to, from, 0);
  35. }
  36.  
  37. int d[10010];
  38.  
  39. int DFS(int u, int flow){
  40. if (u == t) return flow;
  41. int _flow = 0, __flow;
  42. for (int& c_e = cur[u]; c_e != -1; c_e = edges[c_e].next){
  43. int v = edges[c_e].to;
  44. if (d[v] == d[u] + 1 && edges[c_e].dis > 0){
  45. __flow = DFS(v, min(flow, edges[c_e].dis));
  46. flow -= __flow;
  47. edges[c_e].dis -= __flow;
  48. _flow += __flow;
  49. edges[c_e^1].dis += __flow;
  50. if (!flow)
  51. break;
  52. }
  53. }
  54. if (!_flow) d[u] = -1;
  55. return _flow;
  56. }
  57.  
  58. bool BFS(){
  59. memset(d, -1, sizeof(d));
  60. queue<int> que; que.push(s);
  61. d[s] = 0; int u, _new;
  62. while (!que.empty()){
  63. u = que.front(), que.pop();
  64. for (int c_e = head[u]; c_e != -1; c_e = edges[c_e].next){
  65. _new = edges[c_e].to;
  66. if (d[_new] == -1 && edges[c_e].dis > 0){
  67. d[_new] = d[u] + 1;
  68. que.push(_new);
  69. }
  70. }
  71. }
  72. return (d[t] != -1);
  73. }
  74.  
  75. void dinic(){
  76. int max_flow = 0;
  77. while (BFS()){
  78. for (int i = 1; i <= n; ++i) cur[i] = head[i];
  79. max_flow += DFS(s, MAX);
  80. }
  81. printf("%d", max_flow);
  82. }
  83.  
  84. int main(){
  85. n = read(), m = read(), s = read(), t = read();
  86. memset(head, -1, sizeof(head));
  87. for (int i = 0; i < m; i++){
  88. int u = read(), v = read(), w = read();
  89. addEdge(u, v, w);
  90. }
  91. dinic();
  92. return 0;
  93. }

算法主要应用场景

1、裸的最大流

2、二分图的最大匹配:建一个点S,连到二分图的集合A中;建一个点T,连到二分图的集合B中。再将所有的集合A中的点与集合B中的点相连。全部边权设为1,跑一遍最大流,结果即为二分图的最大匹配

3、最小割(定义自行百度):在单源单汇流量图中,最大流等于最小割

4、求最大权闭合图(定义自行百度):最大权值=正点权之和-最小割

主要问题:

  为什么要建立反向边?

  Answer:总结多篇博客,认为建立反向边旨在增加重新调整流的机会,即保障解是最优的(还是没有理解?可以自行百度:D)。

版权申明:未经博主允许禁止转载

网络流Dinic(本篇介绍最大流)的更多相关文章

  1. 再写一篇tps限流

    再写一篇tps限流 各种限流算法的称呼 网上有很多文章介绍限流算法,但是对于这些算法的称呼与描述也是有点难以理解.不管那么多了.我先按我理解的维度梳理一下. 主要维度是:是正向计数还是反向计数.是定点 ...

  2. 国内首篇介绍JanOS物联网操作系统的文章 - 如何把你的手机主板打造成物联网平台

    天地会珠海分舵注:如无意外,您现在正在看的将是国内首篇且是唯一一篇介绍炙手可热的物联网的操作系统JanOS的文章!不信你去百度!希望大家能喜欢.但本文只是引言,更多信息请还是访问JanOS的官网:ht ...

  3. POJ 1273 Drainage Ditches (网络流Dinic模板)

    Description Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover ...

  4. Java并发包下锁学习第一篇:介绍及学习安排

    Java并发包下锁学习第一篇:介绍及学习安排 在Java并发编程中,实现锁的方式有两种,分别是:可以使用同步锁(synchronized关键字的锁),还有lock接口下的锁.从今天起,凯哥将带领大家一 ...

  5. spring cloud系列教程第一篇-介绍

    spring cloud系列教程第一篇-介绍 前言: 现在Java招聘中最常见的是会微服务开发,微服务已经在国内火了几年了,而且也成了趋势了.那么,微服务只是指spring boot吗?当然不是了,微 ...

  6. 老猿学5G扫盲贴:推荐三篇介绍HTTP2协议相关的文章

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 5G中的服务化接口调用都是基于HTTP2协议的,老 ...

  7. 网络流小记(EK&dinic&当前弧优化&费用流)

    欢 迎 来 到 网 络 瘤 的 世 界 什么是网络流? 现在我们有一座水库,周围有n个村庄,每个村庄都需要水,所以会修水管(每个水管都有一定的容量,流过的水量不能超过容量).最终水一定会流向唯一一个废 ...

  8. POJ 2987 Firing 最大流 网络流 dinic 模板

    https://www.cnblogs.com/137shoebills/p/9100790.html http://poj.org/problem?id=2987 之前写过这道题,码一个dinic的 ...

  9. POJ 3281(Dining-网络流拆点)[Template:网络流dinic]

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmlrZTBnb29k/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...

随机推荐

  1. STM32 FSMC使用笔记

    最近在使用STM32的FSMC与FPGA做并行通信总线控制,做一下总结 1,利用FSMC读取写入16位数据时的封装函数如下,不这样使用的话在与FPGA进行通信的过程中可能会出现不可预知的错误. #de ...

  2. bzoj3122 [SDOI2013]随机数生成器

    bzoj3122 [SDOI2013]随机数生成器 给定一个递推式, \(X_i=(aX_{i-1}+b)\mod P\) 求满足 \(X_k=t\) 的最小整数解,无解输出 \(-1\) \(0\l ...

  3. Autoware(2)—加载地图数据

    选择Point cloud.Ref选择.autoware/.data/map/pointcloud_map/里面的全选 点Point cloud加载 vector Map和TF同理

  4. 【Topcoder 1879】Scheduling

    题意:给一个\(dag\),每一个点有一个访问时间. 现在可以同时访问两个点,但当连向这个点的所有点都被访问完成后才可以访问这个点. 问最短访问时间. 思路:一眼贪心.可惜是错的. 第二眼暴搜.就这么 ...

  5. 看门狗芯片--SP706SEN--调试记录

    一.前因后果 工程中,设备为了稳定可靠,会增加外部看门狗,但是外部看门狗一旦启动,就停不下来,必须在固定的时间范围内进行喂狗,不然看门狗芯片就会产生一个复位信号复位MCU.以前大家都认为看门狗一旦工作 ...

  6. [properJavaRDP]在网页中实现远程连接

    内容摘要: 利用开源软件properJavaRDP实现远程桌面连接 如何让Applet嵌入到网页中正常运行 如何处理连接win7时的异常:Wrong modulus size! Expected 64 ...

  7. Django缓存和内置信号

    缓存 简单概括就是将对数据库操作查询所得到的数据放入另外一台机器上(缓存)中,当用户再次请求时,直接去缓存中拿,避免对数据库的频繁操作,加快数据的显示时间,需要知道的是,缓存里面的数据一般都设置有超时 ...

  8. JS 设计模式二 -- 单例模式

    单例模式 概念 单例模式 就是保证一个类只有一个实例,并提供一个访问它的全局访问点. 实现方法 先判断实例是否存在,如果存在直接返回,如果不存在就创建实例后在返回,确保了一个类只有一个实例对象. va ...

  9. 我遇到的Spring的@Value注解失效问题

    项目使用的是SSM体系,spring的配置如下,配置没问题,因为我发现其他文件中的@Value可以使用,只有一处@Value失效了. spring-servlet.xml <?xml versi ...

  10. rsync用法详细解释

    提要 熟悉 rsync 的功能及其特点 掌握 rsync 语法及常用选项的功能 掌握 rsync 命令的三种基本使用方法 掌握如何筛选 rsync 的传输目标 掌握使用 rsync 进行镜像和增量备份 ...