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

news2024/11/25 0:51:53

文章目录

    • 零、前言
    • 一、概念回顾(可略过)
      • 1.1流网络
      • 1.2流
      • 1.3最大流
      • 1.4残留网络
      • 1.5增广路径
      • 1.6流网络的割
      • 1.7最大流最小割定理
        • 1.7.1证明
      • 1.8Ford-Fulkerson方法
    • 二、Dinic算法
      • 2.1EK算法的可优化之处
      • 2.2Dinic算法的优化策略
      • 2.3Dinic算法原理
        • 2.3.1找增广路
        • 2.3.2更新剩余容量
      • 2.4算法流程
      • 2.5代码实现
      • 2.6OJ模板练习

零、前言

关于网络流、最大流基本概念见:最大流—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)Ef(u,x)=(x,v)Ef(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=vVf(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) cf(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 \} Ef={(u,v)V×V:cf(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();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1405712.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浏览器无网

目录 1.运行网络诊断&#xff0c;确认原因 原因A.远程计算机或设备将不接受连接(该设备或资源(Web 代理)未设置为接受端口“7890”上的连接 原因B.DNS服务器未响应 场景A.其他的浏览器可以打开网页&#xff0c;自带的Edge却不行 方法A&#xff1a;关闭代理 Google自带翻译…

<C++>STL->vector

vector的介绍 vector的使用文档 vector是一个可改变数组大小的序列容器vector和数组一样采取连续的空间存放数据&#xff0c;可以使用方括号访问vector的元素&#xff0c;和数组一样高效。但是vector的大小可以动态增长&#xff0c;而数组不行实际上vector内部使用一个动态分…

MySQL-SQL-DQL

DQL-介绍 DQL-语法 基本查询 1、查询多个字段 2、设置别名 3、去除重复记录 条件查询 1、语法 2、条件 聚合函数 1、介绍 2、常见的聚合函数 3、语法 分组查询 1、语法 2、where与having区别 排序查询 1、语法 2、排序方式 分页查询 1、语法 DQL-执行顺序

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效…

C++大学教程(第九版)6.29素数

题目 (素数)素数是只能被1和自已整除的整数。例如,235和7是素数而468和9不是素数 a)编写一个函数&#xff0c;确定一个数是否是素数。 b)在程序中使用这个函数&#xff0c;该程序确定和打印2 ~10000之间的所有素数。在确信已找到所有的素数之前&#xff0c;实际需测试这些数中…

五邑大学餐厅网络点餐系统设计与实现(包含完整源码详细开发过程)

博主介绍&#xff1a;✌专研于前后端领域优质创作者、本质互联网精神开源贡献答疑解惑、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战&#xff0c;深受全网粉丝喜爱与支持✌有需要可以联系作者我哦&#xff01; &#x1f345;文末获…

你不知道的git如何撤销回退版本

简言之&#xff1a;从1 回退到 3&#xff0c;在3版本通过回退记录(git reflog)找到它的上一条回退记录的hash值&#xff0c;复制1的hash值进行回退&#xff0c;执行git reset --hard 粘贴1的hash值进来&#xff0c;此时就回到1的版本了&#xff0c;执行git log即可看到1、2、3、…

安装pytorch GPU的方法,一次安装成功!!win10、win11皆可用!!

前提—查看是否有NVIDIV英伟达显卡&#xff01;&#xff01; 在控制面板打开设备管理器 一、查看电脑的显卡驱动版本 方法一&#xff1a;在cmd命令窗口中输入nvidia-smi&#xff0c;可以发现版本为12.2 方法2&#xff1a;点击NVIDIA控制面板→系统信息 二、安装CUDA 方法1…

算法(4)——前缀和

目录 一、前缀和的定义 二、一维前缀和 三、一维前缀和OJ题 3.1、前缀和 3.2、寻找数组中心下标 3.3、除自身以外数组的乘积 3.4、和为K的数组 3.5、和可被K整除的子数组 3.6、连续数组 四、二位前缀和 4.1、二维前缀和 4.2、矩阵区域和 一、前缀和的定义 对于一个…

探索全球DNS体系 | 从根服务器到本地解析

DNS 发展 DNS&#xff08;Domain Name System&#xff09;的起源可以追溯到互联网早期。 早期的挑战&#xff1a; 早期互联网主要通过IP地址进行通信&#xff0c;用户需要记住复杂的数字串来访问网站。 需求的催生&#xff1a; 随着互联网的扩大&#xff0c;更简单、易记的…

pytest+allure 生成中文报告

背景 已安装pytestallure&#xff0c;生成的报告是英文 allure生成中文报告 参考&#xff1a;allure report 报告中文化及其它优化 方法1&#xff1a;直接在报告中切换中文 方法2&#xff1a;依赖系统中文语言 创建一个setting.js 文件在index.html 同级目录 // 尝试从 l…

java基于安卓开发的流浪动物救助移动应用的设计与实现-计算机毕业设计源码12783

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;流浪动物救助系统被用户普遍使用&#xff0c;为方便用户能…

前端面试题-(BFC,前端尺寸单位,网站页面常见的优化手段)

前端面试题-BFC&#xff0c;前端尺寸单位&#xff0c;网站页面常见的优化手段 BFC前端尺寸单位网站页面常见的优化手段 BFC BFC&#xff08;block formartting context&#xff09;块格式化上下文。是通过独立渲染的区域&#xff0c;它拥有自己的渲染规则&#xff0c;可以决定…

51单片机LCD1602调试工具

参考视频&#xff1a;江协科技51单片机 LCD1602头文件代码 #ifndef __LCD1602_H__ #define __LCD1602_H__//用户调用函数&#xff1a; void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); void LCD_ShowString(unsigned char Line,un…

纷享销客盛邀渠道生态伙伴共赴杭州,凝心聚力共谋未来

2024年1月19日&#xff0c;“凝心聚力 勇立潮头——2024纷享销客首场生态伙伴发展共建会”在杭州绿谷举办。此次会议汇聚了各方60余位伙伴到场&#xff0c;共同探讨行业的未来发展趋势&#xff0c;激发创新力和合作潜力。 会上&#xff0c;纷享销客创始人兼CEO罗旭详尽地介绍了…

Qt Designer教程

文章目录 创建一个 ui 文件选择控件Qt Designer基本控件介绍1、Layouts1.1、Layouts 布局1.2、参数配置 2、Spacers2.1、 Spacers 弹簧介绍2.2、 参数设置 3、Buttons 按键3.1、 Buttons 按键分类 4、Item Views&#xff08;Model-Based&#xff09; 项目视图(基于模型)4.1、 B…

鸿蒙 HarmonyOS ArkTS 弹窗、带点击回调

// xxx.ets@Entry@Componentstruct Page {@State color: Color = Color.Blue;build() {Column({ space: 20 }) {Button(弹窗).width(180).height(80).backgroundColor(this.color).onClick(()=>{AlertDialog.show({title: 弹窗标题,message: 弹窗内容,autoCancel: true,alig…

状态空间模型(SSM)是近来一种备受关注的 Transformer 替代技术

状态空间模型&#xff08;SSM&#xff09;是近来一种备受关注的 Transformer 替代技术&#xff0c;其优势是能在长上下文任务上实现线性时间的推理、并行化训练和强大的性能。而基于选择性 SSM 和硬件感知型设计的 Mamba 更是表现出色&#xff0c;成为了基于注意力的 Transform…

ElasticSearch的集群管理命令

ElasticSearch版本 {"name" : "data-slave1","cluster_name" : "data-es","cluster_uuid" : "xxxxxxxxxx-eMwxw","version" : {"number" : "7.2.1","build_flavor" : &…

重拾计网-第四弹 计算机网络性能指标

ps&#xff1a;本文章的图片内容来源都是来自于湖科大教书匠的视频&#xff0c;声明&#xff1a;仅供自己复习&#xff0c;里面加上了自己的理解 这里附上视频链接地址&#xff1a;1.5 计算机网络的性能指标&#xff08;1&#xff09;_哔哩哔哩_bilibili ​​​ 目录 &#x…