图论 | 网络流的基本概念

news/2024/7/15 17:40:31 标签: 图论, 网络, 网络流

文章目录

    • 流网路
    • 残留网络
    • 增广路径
    • 最大流最小割定理
    • 最大流
      • Edmonds-Karp 算法
        • 算法步骤
        • 程序代码
        • 时间复杂度

流网路

网络 G = ( V , E ) G = (V, E) G=(V,E)

在这里插入图片描述

  • 有向图,不考虑反向边
  • s:源点
  • t:汇点
  • c ( u , v ) c(u, v) c(u,v):边的最大容量
  • 可行流 f f f
    • 容量限制: 0 ≤ f ( u , v ) ≤ c ( u , v ) 0 \leq f(u, v) \leq c(u, v) 0f(u,v)c(u,v)
    • 流量守恒:除了源点和汇点,所有点满足 流入 = 流出 流入 = 流出 流入=流出
  • ∣ f ∣ |f| f:可行流的流量,即从源点流向汇点的速率。一种通用的解释是 从源点流出的流量 − 流入源点的流量 从源点流出的流量 - 流入源点的流量 从源点流出的流量流入源点的流量
  • 最大流:最大可行流

残留网络

残留网络定义:一个可行流流网络 f f f 对应一个残留网络 G f G_f Gf

  • 点集:与原图的点集一样 V f = V V_f = V Vf=V
  • 边集:不仅包含原图的边,同时包含所有边的方向边,即 E f = E 和 E 中的所有反向边 E_f = E 和 E中的所有反向边 Ef=EE中的所有反向边
  • 边的容量: c f ( u , v ) c_f(u, v) cf(u,v)
    • 原图中的边:剩下的容量,即 c ( u , v ) − f ( u , v ) c(u, v) - f(u, v) c(u,v)f(u,v)
    • 反向边:可以退回的流量,即 f ( v , u ) f(v, u) f(v,u)

重要结论:原网络的可行流 f f f 加上可行流对应的残留网络 G f G_f Gf,也是一个可行流

  • 对应边相加:若方向同则相加;若反向反则相减
  • 结论: ∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ |f + f'| = |f| + |f'| f+f=f+f
  • 进一步,若残留网络没有可行流,那么原网络的可行流就一定是最大流

增广路径

在残留网络里,如果沿着容量大于 0 的边走,能走到汇点,则这条路径叫做增广路径

  • 若存在一个增广路径,根据 ∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ |f + f'| = |f| + |f'| f+f=f+f,原来的可行流一定不是最大流
  • 若不存在增广路径,我们可以得出当前可行流就是最大流

将点集 V 分成 S 和 T 两个子集

  • 分割要满足 S ∪ T = V , S ∩ T = ∅ S ∪ T = V, S ∩ T = \emptyset ST=VST=
  • 点集不一定连通

割的容量: c ( S , T ) = ∑ u ∈ S ∑ v ∈ T c ( u , v ) c(S, T) = \sum_{u ∈ S} \sum_{v ∈ T} c(u, v) c(S,T)=uSvTc(u,v)

  • 最小割:最小割的容量
  • 割的容量不考虑反向边

割的流量: f ( S , T ) = ∑ u ∈ S ∑ v ∈ T f ( u , v ) − ∑ u ∈ T ∑ v ∈ S f ( u , v ) f(S, T) = \sum_{u ∈ S} \sum_{v ∈ T} f(u, v) - \sum_{u ∈ T} \sum_{v ∈ S} f(u, v) f(S,T)=uSvTf(u,v)uTvSf(u,v)

  • 流过去的流量减去流过来的流量
  • 割的流量考虑反向边

重要性质:

  • 对于任意一个割,割的流量一定小于等于割的容量,即 f ( S , T ) ≤ c ( S , T ) f(S, T) \leq c(S, T) f(S,T)c(S,T)

  • 割的流量等于原流网络的流量,即 f ( S , T ) = ∣ f ∣ f(S,T) = |f| f(S,T)=f

  • f ( X , Y ) = − f ( Y , X ) f(X, Y) = -f(Y, X) f(X,Y)=f(Y,X)

  • f ( Z , X ∪ Y ) = f ( Z , X ) + f ( Z , Y ) f(Z, X ∪ Y) = f(Z, X) + f(Z, Y) f(Z,XY)=f(Z,X)+f(Z,Y)

  • f ( X ∪ Y , Z ) = f ( X , Z ) + f ( Y , Z ) f(X ∪ Y, Z) = f(X, Z) + f(Y, Z) f(XY,Z)=f(X,Z)+f(Y,Z)

最大流最小割定理

以下三个条件是等价的

  1. 可行流 f f f 是最大流
  2. 可行流 f f f 的残留网络中不存在增广路
  3. 存在某个割 [ S , T ] [S, T] [S,T] ∣ f ∣ = c ( S , T ) |f| = c(S, T) f=c(S,T)

最大流

Edmonds-Karp 算法

算法步骤

维护流网络的残留网络,不断进行以下流程:

  1. 找一条增广路 f ′ f' f:可以用 BFS 进行搜索
  2. 更新残留网络 G f → G f + f ′ G_f → G_{f + f'} GfGf+f
程序代码
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1010, M = 20020, INF = 1e8;

// 邻接表存储残留网络
// 正向边和反向边成对存在,正向边的下标异或上1得到方向边的下标
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;  // f表示容量
int q[N], d[N], pre[N];
bool st[N];  // 避免重复搜索

void add(int a, int b, int c)
{
    // 正向边 
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
    // 反向边,初始容量为0
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}

// bfs找增广路
bool bfs()
{
    int hh = 0, tt = 0;
    memset(st, false, sizeof(st));
    q[0] = S, st[S] = true, d[S] = INF;
    while(hh <= tt) {
        // 从队列中弹出一个元素进行BFS
        int t = q[hh++];
        for(int i = h[t]; ~i; i = ne[i]) {
            // 节点t的临接边i的下一节点ver
            int ver = e[i];
            // 没遍历过且边i的容量不为0
            if( !st[ver] && f[i] ) {
                st[ver] = true;
                // 流到节点ver的流量为流到t的流量和边i容量的最小值
                d[ver] = min(d[t], f[i]);
                // 记录节点ver前驱边的编号
                pre[ver] = i;
                if(ver == T)  return true;
                // ver入队
                q[++tt] = ver;
            }
        }
    }
    return false;
}

// EK 算法
int EK()
{
    int r = 0;
    while( bfs() ) {
        // 加上增广路的流量
        r += d[T];
        // 更新残留网络
        for(int i = T; i != S; i = e[pre[i] ^ 1]) {
            // 正向边更新
            f[pre[i]] -= d[T];
            // 反向边更新
            f[pre[i] ^ 1] += d[T];
        }
    }
    return r;
}

int main()
{
    // 点数、边数、源点、汇点
    cin >> n >> m >> S >> T;
    // 初始化邻接表
    memset(h, -1, sizeof(h));
    while( m-- ) {
        int a, b, c;
        // 边ab的容量为c
        cin >> a >> b >> c;
        add(a, b, c);
    }
    
    cout << EK() << endl;
    return 0;
}
时间复杂度

O ( V E 2 ) O(VE^2) O(VE2)


http://www.niftyadmin.cn/n/5279286.html

相关文章

[VScode]Jupyter自动生成目录

用到插件Jupyter TOC 插件主页有一张动图介绍这个功能怎么用&#xff1a; 点击标签页面&#xff0c;右上角3个点那个位置&#xff0c;不是正文里单元格的右上角&#xff1b; 选Generate table of contents就可以自动生成目录 VScode 还有好多好多功能等待你发现呢&#xff0…

【数据结构和算法】定长子串中元音的最大数目

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 方法一&#xff1a;滑动窗口 2.2 方法二&#xff1a;滑动窗口优化版 三、代码 3.1 方法一&#xf…

《open3D+pyqt》第二章——CSF(布料模拟算法)

《open3D+pyqt》第二章——CSF布料模拟算法 一、效果展示二、qt设置及相关教程三、核心代码一、效果展示 二、qt设置及相关教程 参考博客 :CSF 注:csf经常用于处理las点云,本文安装laspy添加las格式点云,具体教程看IO部分教程 步骤1:建立UI pyuic5 -o filter_csf_rc

35_36-Golang 中的 go mod 以及 Golang 包详解

**Golang **中的 **go mod **以及 **Golang **包详解 主讲教师&#xff1a;&#xff08;大地&#xff09; 合作网站&#xff1a;www.itying.com** **&#xff08;IT 营&#xff09; 我的专栏&#xff1a;https://www.itying.com/category-79-b0.html 一、Golang 中包的介绍和…

苏州耕耘无忧物联网:降本增效,设备维护管理数字化转型的引领者

随着科技的快速发展和工业4.0的推动&#xff0c;设备维护管理已经从传统的被动式、经验式维护&#xff0c;转向了更加积极主动、数据驱动的维护模式。在这个过程中&#xff0c;苏州耕耘无忧物联科技有限公司以其深厚的技术积累和丰富的管理经验&#xff0c;引领着设备维护管理数…

蓝桥杯c/c++程序设计——冶炼金属

冶炼金属 问题描述 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V&#xff0c;V 是一个正整数&#xff0c;这意味着消耗 V 个普通金属 O 恰好可以冶炼出一个特殊金属 X&#xff0c;当普通金属 O 的数目不足 V 时&#xff0…

飞凌嵌入式受邀亮相2023统信UOS生态大会,开启国产OS新探索

12月20日&#xff0c;“2023操作系统产业大会暨统信UOS生态大会”&#xff08;以下简称“统信UOS生态大会”&#xff09;顺利举办&#xff0c;飞凌嵌入式作为国内领先的智能设备核心平台研发制造企业受到邀请&#xff0c;亮相北京主会场。 作为统信软件面向IT产业规格最高、规…

flutter开发windows应用的库

一、window_manager 这个插件允许 Flutter 桌面应用调整窗口的大小和位置 地址&#xff1a;https://github.com/leanflutter/window_manager二、win32 一个包&#xff0c;它使用FFI包装了一些最常见的Win32 API调用&#xff0c;使Dart代码可以访问这些调用&#xff0c;而不需…