最大流-Dinic算法,原理详解,四大优化,详细代码

2024-01-24

零、前言

关于网络流、最大流基本概念见: 最大流—EK算法,流网络,残留网络,定理证明,详细代码-CSDN博客


一、概念回顾(可略过)

已经在 最大流—EK算法,流网络,残留网络,定理证明,详细代码-CSDN博客 中介绍过

1.1流网络

流网络 G = (V , E)是一个有向图,其中每条边(u , v)∈E均有一 非负容量c(u , v) ≥ 0。如果(u , v) ∉ E,则c(u , v) = 0。

流网络 中有两个特别的点: 源点s 汇点t 如例中的工厂和仓库。

1.2流

设f(x , y)是定义在节点二元组(x∈V , y∈V)上的实数函数,且满足:

  1. 容量限制 : f ( x , y ) ≤ c ( x , y ) 容量限制:f(x , y) \le c(x , y) 容量限制 : f ( x , y ) c ( x , y )

  2. 反对称性 : f ( x , y ) = − f ( y , x ) 反对称性:f(x , y) = -f(y , x) 反对称性 : f ( x , y ) = f ( y , x )

  3. 流守恒性 : ∀ x ≠ s , x ≠ t , ∑ ( u , x ) ∈ E f ( u , x ) = ∑ ( x , v ) ∈ E f ( x , v ) 流守恒性:\forall x \ne s,x \ne t,\sum_{(u,x)\in E}f(u,x)=\sum_{(x,v)\in E}f(x,v) 流守恒性 : x = s , x = t , ( u , x ) E f ( u , x ) = ( x , v ) E f ( x , v )

f(x,y) 称为流网络的 流函数 , 对于(x , y)∈E , f(x , y) 称为 边的流量 , c(x , y) - f(x , y)称为边的剩余流量 .

流 f 值的定义为
∣ f ∣ = ∑ v ∈ V f ( s , v ) \left |f \right | = \sum_{v\in V}f(s,v) f = v V f ( s , v )
亦即 , 从源点s发出的总流 。如例中每天从工厂发出的冰球的箱数。

1.3最大流

对于一个给定的流网络 , 合法的流函数 f 有很多. 使得流的值最大的流函数被称为网络的最大流 , 此时的 流的值被称为网络的最大流量 .

1.4残留网络

假定有一个流网络G = (V , E),其源点为s,汇点为t。设f为G中的一个流,一对顶点u, v∈V。 在不超过容量c(u,v)的条件下 从u到v之间可以压入的额外网络流量 ,就是**(u,v) 的残留容量(residual capacity)**,由下式定义:
c f ( u , v ) = c ( u , v ) − f ( u , v ) c_{f}(u,v) = c(u,v) - f(u,v) c f ( u , v ) = c ( u , v ) f ( u , v )

给定一流网络G = (V , E)和流f,由f压得的G的残留网络是Gf = (V , Ef),其中:
E f = { ( u , v ) ∈ V × V : c f ( u , v ) > 0 } E_{f} = \{ (u,v)\in V\times V:c_{f}(u,v)>0 \} E f = {( u , v ) V × V : c f ( u , v ) > 0 }
即,残留网络包含了流网络的所有点,和残留容量大于0的有向边。

注意Ef中的边 既可以是E中的有向边也可以是其反向边 ,若(u , v)∈E,有f(u , v) < c(u , v),那么根据流网络的性质可知f(v , u) = -f(u,v),那么对应残留容量就是c(v , u) - (-f(u,v)) = c(v , u) + f(u , v) > 0,则其反向边也在残留网络中。

由残留网络可以得出 引理

f 为G中的一个流,f‘为Gf中的一个流,那么f + f’仍为流网络G的一个流,其流量为| f + f’ | = | f | + | f‘ |

具体证明可以自己尝试或见《算法导论》

1.5增广路径

已知流网络G = (V , E)和流f, 增广路径p 残留网络Gf 中由 源点s到汇点t的一条简单路径

根据残留网络的定义,增广路径上的每条边的剩余容量都大于0,则该路径上的每条边都可以额外容纳一定的流量,这也和我们后续求最大流密切相关。

不难想出, 增广路径可以增加的最大流量为该路径上边的最小残留容量

1.6流网络的割

流网络G = (V , E)的割(S , T) 将V划分为S和T两部分,使得s∈S,t属于T,通过 割的流量为S和T之间边上流量的代数和 ,但是 割的容量仅包含从S到T的边的容量的代数和

如下图,割(S,T)的流量f(S,T) = 12 - 4 + 11 = 19

容量c(S,T) = 12 + 14 = 26

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们称 容量最小的割为最小割

可以证明f(S , T) = | f | ≤ c(S, T)(证明见《算法导论》)

1.7最大流最小割定理

如果 f是具有源点s和汇点t的流网络G = (V , E)中的一个流,那么下列条件是等价的:

  1. f是G的一个最大流
  2. 残留网络Gf不包含增广路
  3. 存在G的某个割(S , T),有| f | = c(S , T)
1.7.1证明

采用循环证明法,(1) => (2) , (2) => (3) , (3) => (1)

(1) => (2):

很容易证明,采用反证法即可

假设Gf含增广路,那么我们可以在Gf中构造一流f’,| f‘ | = min(cf(u,v) , (u , v) ∈ Ef),那么f + f’仍为流网络的一个流(由1.4中介绍的引理可知),那么|f + f‘| > | f |,那么f就不是最大流,矛盾,则(1) => (2)成立

(2) => (1):

我们只需要在(2)的条件下构造出一个满足(3)的割即可。

选取集合S = {v ∈ V:Gf中从s到v存在一条通路},T = V - S,划分(S , T)为一个割。

对所有<u , v>,u∈S,v∈T,f(u , v) = c(u , v),否则v就属于S。

由此推出| f | = f(S , T) = c(S , T)

(3) => (1):

也很容易证明,由于由于| f | ≤ c(S, T),而此时| f | = c(S , T),故不存在比f更大的流,故f为最大流。

1.8Ford-Fulkerson方法

Ford-Fulkerson方法 是最大流的经典求解方法,之所以称之为”方法“而非”算法“,是由于它包含具有不同运行时间的几种实现。

Ford-Fulkerson方法 依赖于三种重要思想: 残留网络(residual network)、增广路(augmenting path)和割(cut)。 这些思想是 最大流最小割定理的精髓 ,这里给出 Ford-Fulkerson方法 的特定实现。

伪代码如下:

Ford-Fulkerson(G , s , t)
初始化流f = 0
while 流网络中存在增广路p
	do 沿着p增加f
return f

二、Dinic算法

2.1EK算法的可优化之处

Dinic算法其实就是对于EK算法的优化。

无论是Dinic算法还是EK算法它们都是基于FF方法来求最大流,我们回看EK算法的实现原理:

  • bfs找到 一条 增广路
  • 更新增广路上边的剩余容量
  • 找不到增广路,算法结束,时间复杂度O(n^2 m)

很明显,EK算法有个问题就是 每次只找一条增广路 然后进行更新,然后再从源点开始找增广路,我们有如下疑问:

  • 我们能否一次更新多条增广路的剩余容量呢?
  • 对于网络中的多条增广路,我们如何尽可能快的找到增广路呢?

2.2Dinic算法的优化策略

  1. 搜索顺序优化:根据每个点到源点距离,建立分层图,bfs一层一层地去增广,对于不在当前bfs层的下一层的点不进行进一步搜索,避免往回来回走,降低效率
  2. 当前弧优化:dfs更新多条增广路容量时,对于每个顶点发出的边,其所在增广路已经更新过的边进行剪枝
  3. 剩余容量优化:很容易理解,能够增加的流量用完了,就剪枝
  4. 残枝优化:如果该点不在增广路上就剪枝

1是在bfs中的优化,比较直观,2、3、4是在dfs更新容量中的优化,可以结合后续代码理解

2.3Dinic算法原理

2.3.1找增广路

仍然是bfs往下搜索,直到遇到汇点t,只不过我们枚举每个点u时,将其未访问过的邻接点v的深度d[v]设置为d[u] + 1,即一层一层往下找,后面更新也是一层一层往下更新回溯

2.3.2更新剩余容量

EK算法更新剩余容量的策略是记录增广路上节点的前驱节点,从汇点往前一个一个更新

我们Dinic算法更为高效,对一个点发出去的多条增广路上的点都进行更新,对于要更新的点给其一个容量限制limit,即最多能增加的流量,对于每个邻接点v对其深搜,给v的limit为min(limit , w(u,v)),然后获取深搜v得到的v发出的所有增广路能够增加的流量和incf,对边(u,v)的剩余容量进行更新,当然limit也要相应减少

可见dinic是通过深搜加回溯完成了多条增广路的剩余容量更新。

2.4算法流程

  • dinic算法不断重复以下步骤,直到在残留网络中无法到达t
    • bfs建立分层图的同时找增广路
    • dfs从源点开始遍历增广路,回溯时更新剩余容量,初始给源点s的可增加流量上限limit为inf(无穷大)
  • 时间复杂度O(nm^2),实际中远达不到这个上界,可以解决1e4量级的数据

2.5代码实现

#define int long long
#define N 205
#define M 5005
const int MOD = 10000007;
const int inf = 0x3f3f3f3f3f3f3f3f;

int n, m, s, t, idx;
int d[N], cur[N], head[N]; // 深度,当前边,前向星头

struct edge
{
    int v, c, nxt;
} edges[M << 1];

inline void addedge(int u, int v, int c)
{
    edges[idx] = {v, c, head[u]};
    head[u] = idx++;
}

bool bfs() // 多路增广,分层搜索优化
{
    memset(d, 0, sizeof(d));
    queue<int> q;
    q.emplace(s), d[s] = 1;
    while (q.size())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = edges[i].nxt)
        {
            int v = edges[i].v;
            if (!d[v] && edges[i].c)
            {
                d[v] = d[u] + 1;
                q.emplace(v);
                if (v == t)
                    return true;
            }
        }
    }
    return false;
}

int dfs(int u, int limit)
{
    if (u == t)
        return limit;
    int ret = 0;
    for (int i = cur[u]; ~i && limit > 0 ; i = edges[i].nxt)//limit > 0 余量优化
    {
        cur[u] = i;// 当前弧优化
        int v = edges[i].v;
        if (d[v] == d[u] + 1 && edges[i].c)
        {
            int incf = dfs(v, min(limit, edges[i].c));
            if (!incf)
                d[v] = 0; //剪枝优化
            edges[i].c -= incf, edges[i ^ 1].c += incf, ret += incf, limit -= incf;
        }
    }
    return ret;
}

int dinic()
{
    int ret = 0;
    while (bfs())
        memcpy(cur, head, sizeof(head)), ret += dfs(s, inf);

    return ret;
}

2.6OJ模板练习

P3376 【模板】网络最大流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

直接跑板子即可,这道题数据量过小,EK算法也能过

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <queue>
#include <unordered_set>
#include <map>
#include <bitset>
using namespace std;
#define sc scanf
#define int long long
#define N 205
#define M 5005
const int MOD = 10000007;
const int inf = 0x3f3f3f3f3f3f3f3f;

int n, m, s, t, idx;
int d[N], cur[N], head[N]; // 深度,当前边,前向星头

struct edge
{
    int v, c, nxt;
} edges[M << 1];

inline void addedge(int u, int v, int c)
{
    edges[idx] = {v, c, head[u]};
    head[u] = idx++;
}

bool bfs() // 多路增广,分层搜索优化
{
    memset(d, 0, sizeof(d));
    queue<int> q;
    q.emplace(s), d[s] = 1;
    while (q.size())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = edges[i].nxt)
        {
            int v = edges[i].v;
            if (!d[v] && edges[i].c)
            {
                d[v] = d[u] + 1;
                q.emplace(v);
                if (v == t)
                    return true;
            }
        }
    }
    return false;
}

int dfs(int u, int limit)
{
    if (u == t)
        return limit;
    int ret = 0;
    for (int i = cur[u]; ~i && limit > 0 ; i = edges[i].nxt)//limit > 0 余量优化
    {
        cur[u] = i;// 当前弧优化
        int v = edges[i].v;
        if (d[v] == d[u] + 1 && edges[i].c)
        {
            int incf = dfs(v, min(limit, edges[i].c));
            if (!incf)
                d[v] = 0; //剪枝优化
            edges[i].c -= incf, edges[i ^ 1].c += incf, ret += incf, limit -= incf;
        }
    }
    return ret;
}

int dinic()
{
    int ret = 0;
    while (bfs())
        memcpy(cur, head, sizeof(head)), ret += dfs(s, inf);

    return ret;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    // freopen("in.txt", "r", stdin);
    cin >> n >> m >> s >> t;
    int a, b, c;
    memset(head, -1, sizeof(head));
    for (int i = 0; i < m; i++)
        cin >> a >> b >> c, addedge(a, b, c), addedge(b, a, 0);
    cout << dinic();
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

最大流-Dinic算法,原理详解,四大优化,详细代码 的相关文章

随机推荐

  • 题解 | #翻转单词序列# 复习Java几个集合方法使用

    人生第一次面试gg 字节跳动 复活赛 二面 字节后端实习面试 字节飞书后端一面凉经 字节 客服平台 一面面经 已挂 菜鸟前端 避雷避雷中华财险 中厂大厂到底怎么选 樊登读书精准营销岗面经 跨行 转技术岗进大厂的好机会 来看 岗位名称 云 软
  • sychnorized积累

    sychnorized 1 对象锁 包括方法锁 默认锁对象为this 当前实例对象 和同步代码块锁 自己指定锁对象 2 类锁 指synchronize修饰静态的方法或指定锁对象为Class对象 3 加锁和释放锁的原理 现象 时机 内置锁th
  • 两个月进口猛增10倍,买近百台光刻机,难怪ASML不舍中国市场

    据统计数据显示 2023年11月和12月 中国从荷兰进口的光刻机设备同比猛增10倍 进口金额超过19亿美元 让ASML赚得盆满钵满 ASML早前表示中国客户在2023年订购的光刻机全数交付 2023年11月中国进口的光刻机达到42台 进口金
  • Cortex-M3与M4权威指南

    处理器类型 所有的ARM Cortex M 处理器是32位的精简指令集处理器 它们有 32位寄存器 32位内部数据路径 32位总线接口 除了32位数据 Cortex M处理器也可以有效地处理器8位和16位数据以及支持许多涉及64位数据的操作
  • 高精度运算合集,加减乘除,快速幂,详细代码,OJ链接

    文章目录 零 前言 一 加法 高精度加法步骤 P1601 A B 二 减法 高精度减法步骤
  • 每日变更的最佳实践

    在优维公司内部 我们采用发布单的方式进行每天的应用变更管理 这里给各位介绍优维的最佳实践 变更是需要多角色合作的 而且他是整体研发流程的一部分 在优维内部 我们坚持每日变更 打通开发环节到最终发布上线的全过程 在保证质量的前提下 尽可能提升
  • 【js学习之路】遍历数组api之 `filter `和 `map`的区别

    一 前言 数组是我们在项目中经常使用的数据类型 今天我们主要简述作用于遍历数组的api filter 和 map 的区别 二 filter和map的共同点 首先 我们主要阐述一下 filter 和 map 的共同点 api的参数都是回调函数
  • 肿瘤的转录调控:Cell子刊揭示原发性肝癌中转录因子活性的全基因组图谱|国自然热点

    转录调控的研究历史比较长 相关研究在近十年来仍一直增长 也是近年来高分文章的焦点之一 在2023年最佳国自然 中标 研究热点 转录调控中标率高达189 作为国自然热点之一的肿瘤微环境的研究在近几年也一直处于上升趋势 转录调控在肿瘤发生 发展
  • 高中数学:因式分解(初接高)

    一 乘法公式 二 十字相乘法 例题 三 增添项法 主要解决整式中含高次项的因式分解题 补充 由于数学笔记 用键盘敲实在是麻烦 这里就把我的笔记截图上来了 大家将就看 有看不清楚的地方 可评论 定回复
  • 实力认证!鼎捷软件荣膺“领军企业”和“创新产品”两大奖项

    近日 由中国科学院软件研究所 中科软科技股份有限公司联合主办的 2023中国软件技术大会 于北京成功举办 本届大会以 大模型驱动下的软件变革 为主题 数十位来自知名互联网公司和软件巨头企业的技术大咖 不同领域行业专家 畅销书作者等分享嘉宾
  • 在 Python 中实现 List 抽象

    在 Python 中 创建一个包含多个对象的 list 很常见 例如 对于一组具有相同功能的对象 比如播放声音 希望能够使用类似 my list play 的语法来触发 list 中所有对象的 play 方法 另一个例子是 当希望关闭 li
  • Eggplant—HMI自动化测试软件

    产品概述 Eggplant是英国TestPlant公司推出的创新性自动化测试工具 通过VNC或RDP通讯技术远程桌面连接被测对象 基于图像和文字识别算法进行对象定位 进而驱动和确认被测HMI设备的响应 能够实现自动化的HMI操作测试 较大提
  • SAP ERP系统是什么?SAP好用吗?

    A公司是一家传统制造企业 公司曾先后使用过数个管理软件系统 但各部门使用的软件都是单独功能 导致企业日常管理中数据流与信息流相对独立 形成了 信息孤岛 随着公司近年业务规模的快速发展以及客户数量的迅速增加 企业原有的信息系统在销售预测及生产
  • Java开发中不要使用受检异常

    简介 Java是唯一 主流 实现了受检异常概念的编程语言 一开始 受检异常就是争议的焦点 在当时被视为一种创新概念 Java于1996年推出 如今却被视不良实践 本文要讨论Java中非受检异常和受检异常的动机以及它们优缺点 与大多数关注这个
  • Winform中设置程序开机自启动(修改注册表和配置自启动快捷方式)

    场景 winform程序需要在启动时自启动 可通过将exe快捷方式添加到自启动目录下 或者通过修改注册表添加启动项的方式 注 博客 霸道流氓气质 CSDN博客 实现 使用添加快捷方式到启动目录的方式 Windows下怎样使用bat设置Red
  • 服务器中E5和I9的区别是什么,如何选择合适的配置

    随着科技的进步 服务器处理器的性能在不断攀升 其中 Intel的E5和I9系列处理器在业界具有广泛的影响力 而当我们在选择服务器的时候会有各种各样的配置让我们眼花缭乱不知道该怎么去选择 下面我跟大家分享一下E5跟I9有什么区别 方便我们在选
  • 如何应对Android面试官-> 玩转 ViewPager 懒加载

    前言 ViewPager 缓存页面与预加载机制 通常我们 ViewPager 在使用的是一般都是结合 Fragment 一起使用 我们先来搭一个简单的使用界面 最终搭建出来的效果如下 简单的 ViewPager Fragment 的实现 比
  • 项目文章 | IF=8.4&转录因子Egr-1是脑膜炎型大肠杆菌引起的血脑屏障损伤的关键调节因子

    2024年1月17日华中农业大学动科动医学院陈焕春院士 王湘如教授团队在期刊 Cell Communication and Signaling IF 8 4 发表了题为 Egr 1 is a key regulator of the blo
  • 【安全】简单解析统一身份认证:介绍、原理和实现方法

    深入解析统一身份认证 介绍 原理和实现方法 导语 统一身份认证是什么 统一身份认证的原理 统一身份认证的实现 结语 导语 随着互联网的发展和各种在线服务的普及 用户在不同的应用和平台上需要进行多次身份验证 为了简化用户的登录和减少重复操作
  • 最大流-Dinic算法,原理详解,四大优化,详细代码

    文章目录 零 前言 一 概念回顾 可略过 1 1流网络 1 2流 1 3最大流 1 4残留网络 1 5增广路