网络流基本概念及实现算法

news2025/3/20 18:00:42

基本概念

流网络

在这里插入图片描述
对于一个有向图, 抽象成水管里的水的模型, 每根管子有容量限制, 计为 G = ( V , E ) G = (V, E) G=(V,E), 首先不考虑反向边

在这里插入图片描述
对于任意无向图, 都可以将反向边转化为上述形式

如果一条边不存在, 定义为容量为 0 0 0, 形式上来说就是 c ( u , v ) = 0 c(u, v) = 0 c(u,v)=0

可行流

对于每一个流网络考虑一个可行流 f f f, 指定每条边的流量, 需要满足两个条件

  • 容量限制 0 ≤ f ( u , v ) ≤ c ( u , v ) 0 \le f(u, v) \le c(u, v) 0f(u,v)c(u,v)
  • 流量守恒, 对于每个点来说, 流进去多少, 流出来多少, 形式化来说

∑ ( v , u ) f ( v , u ) = ∑ ( u , v ) f ( u , v ) \sum_{(v, u)} f(v, u) = \sum _{(u, v)} f(u, v) (v,u)f(v,u)=(u,v)f(u,v)
对于一个可行流, 从源点流向汇点的流量定义为 ∣ f ∣ |f| f, 每秒从源点流出的流量, 或者流入汇点的流量, 每秒净往外流出的流量

∣ f ∣ = ∑ ( s , v ) f ( s , v ) − ∑ ( v , s ) f ( v , s ) |f| = \sum _{(s, v)} f(s, v) - \sum _{(v, s)} f(v, s) f=(s,v)f(s,v)(v,s)f(v,s)

最大流指的是流量值最大的可行流

残存网络

残存网络是对于流网络某一个可行流
在这里插入图片描述
对于某个可行流 f 1 f_1 f1, 残存网络 G f 1 G_{f_1} Gf1
残存网络的点集与原图完全一致 V f = V V_f = V Vf=V, 但是边集不仅包含原图的边还包含原图的反向边 E f = E + E ′ E_f =E + E ' Ef=E+E

残存网络也是流网络

对于残存网络的容量记为 c ′ ( u , v ) c'(u, v) c(u,v)

  • 对于原图的边, 那么 c ′ ( u , v ) = c ( u , v ) − f ( u , v ) c'(u, v) = c(u, v) - f(u, v) c(u,v)=c(u,v)f(u,v)
  • 对于原图的反向边, c ′ ( u , v ) = f ( v , u ) c'(u, v) = f(v, u) c(u,v)=f(v,u)

对于任意一个可行流 f f f, 都可以求出残存网络 G f G_{f} Gf

记残存网络的可行流 f ′ f' f, f + f ′ f + f' f+f也是原来流网络 G G G的一个可行流

新的流的流量值等于两个流的流量值之和 ∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ |f + f'| = |f| + |f'| f+f=f+f, 流量相加等同于每条边相加

为什么新的流是原流网络 G G G的可行流?

  • 是否满足容量限制
  • 是否满足流量守恒

增广路径

残存网络中, 从源点出发沿着**容量大于 0 0 0**的点走如果能走到汇点, 那么这一条路径就是增广路径
在这里插入图片描述

上述红色路径就是增广路径, 增广路径一定是一个可行流, 流量大于 0 0 0
如果 G f G_f Gf总不存在增广路径, 断言 f f f是原来流网络的最大流

将点集 V V V分为两个集合 S , T S, T S,T, 满足以下条件

  • s ∈ S s \in S sS, t ∈ T t \in T tT
  • S ∪ T = V S \cup T = V ST=V, S ∩ T = ∅ S \cap T = \emptyset ST=

在这里插入图片描述
上述就是割的形式化描述

割的容量

在这里插入图片描述
所有从 S S S指向 T T T的边, 被称为割的容量, 容量不考虑回来的边

c ( S , T ) = ∑ u ∈ S , v ∈ T c ( u , v ) c(S, T) = \sum _{u\in S, v \in T} c(u, v) c(S,T)=uS,vTc(u,v)

割的流量

所有从 S S S留到 T T T的流量减去从 T T T流向 S S S的流量, 也就是净流量

f ( S , T ) = ∑ u ∈ S , v ∈ T f ( u , v ) − ∑ u ∈ T , v ∈ S f ( u , v ) f(S, T) = \sum _{u \in S, v \in T} f(u, v) - \sum _{u \in T, v \in S} f(u, v) f(S,T)=uS,vTf(u,v)uT,vSf(u,v)

对于一个流网络来说, 如果流网络确定, 那么割的容量确定, 但是因为一个流网络存在多个可行流, 因此割的流量是取决于每个可行流的流量的

性质1: 最小割指的是最小割的容量, 对于任意一个割以及任意一个可行流, 割的流量小于等于割的容量, 形式化来说 f ( S , T ) ≤ c ( S , T ) f(S, T) \le c(S, T) f(S,T)c(S,T), 证明过程如下
在这里插入图片描述
性质2: 对于流网络的任意一个割和任意一个可行流都有 f ( S , T ) = ∣ f ∣ f(S, T)= |f| f(S,T)=f

根据性质1和性质2推出 ∣ f ∣ = f ( S , T ) ≤ c ( S , T ) |f| = f(S, T) \le c(S, T) f=f(S,T)c(S,T), 也就推出 ∣ f ∣ ≤ c ( S , T ) |f| \le c(S, T) fc(S,T), 也就是最大流小于等于最小割

最大流最小割定理

对于某个流网络 G = ( V , E ) G = (V, E) G=(V,E), 以下三个结论相互等价

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

证明如下

1 1 1 2 2 2
反证法, 存在增广路径, 由上述增广路径定理可知, 当前可行流不是最大流

3 3 3 1 1 1
因为 ∣ f ∣ ≤ c ( S , T ) |f| \le c(S, T) fc(S,T), 对于结论 3 3 3, 存在一个割使得 ∣ f ∣ = c ( S , T ) |f| = c(S, T) f=c(S,T), 证明 f f f是否是最大流
最大流 ≥ ∣ f ∣ 最大流 \ge |f| 最大流f, 又因为 ∣ f ∣ = c ( S , T ) ≥ 最大流 |f| = c(S, T) \ge 最大流 f=c(S,T)最大流, 因此 f f f是最大流

由结论 3 3 3得知, 最小割 ≤ c ( S , T ) = ∣ f ∣ ≤ 最大流 最小割 \le c(S, T) = |f| \le 最大流 最小割c(S,T)=f最大流, 也就有最小割小于等于最大流, 上述性质2可知, 最大流小于等于最小割, 因此最大流等于最小割

2 2 2 3 3 3
如果当前残存网络不存在增广路径, 能否构造出一个, 使得 c ( S , T ) = ∣ f ∣ c(S, T) = |f| c(S,T)=f
构造一个合法的割
点集 S S S是在 G f G_f Gf s s s沿着容量大于 0 0 0的边走, 走到的所有点, 因为不存在增广路径, 因此 s s s走不到 t t t
点集 T T T V − S V - S VS
借助的 G f G_f Gf构造的是原网络 G G G里的割

判断当前割的容量是否等于 ∣ f ∣ |f| f
在这里插入图片描述
构造的割的性质如下

  1. f ( x , y ) = c ( x , y ) f(x, y) = c(x, y) f(x,y)=c(x,y)
  2. f ( a , b ) = 0 f(a, b) = 0 f(a,b)=0

对于性质1, 如果 f ( x , y ) < c ( x , y ) f(x, y) < c(x, y) f(x,y)<c(x,y), 那么在残存网络中仍然会有 x x x连到 y y y的边, 也就 x x x能遍历到, y ∈ S y \in S yS, 与我们构造的矛盾
对于性质2, 如果 f ( a , b ) > 0 f(a, b) > 0 f(a,b)>0, 那么在残存网络中也是能遍历到, a ∈ S a \in S aS, 与我们构造的矛盾

∣ f ∣ = ∑ u ∈ S , v ∈ T f ( u , v ) − ∑ u ∈ T , v ∈ S f ( u , v ) |f| = \sum _{u \in S, v \in T} f(u, v) - \sum _{u \in T, v \in S} f(u, v) f=uS,vTf(u,v)uT,vSf(u,v)

因为没有反向边, 并且每个边的流量等于边的容量, 因此

∣ f ∣ = ∑ u ∈ S , v ∈ T c ( u , v ) = c ( S , T ) |f| = \sum _{u \in S, v \in T} c(u, v) = c(S, T) f=uS,vTc(u,v)=c(S,T)

最大流最小割定理证毕

最大流算法实现

F o r d – F u l k e r s o n Ford–Fulkerson FordFulkerson方法

求最大流的贪心方法, 维护残存网络, 不断的在残存网络中寻找增广路径, 将当前流变为 f + f ′ f + f' f+f, 然后在新的流的残存网络中继续寻找增广路径, 将当前残存网络 G f G_f Gf变为 G f + f ′ G_{f + f'} Gf+f

在这里插入图片描述
上图是残存网络中更新的过程, k k k是路径的流量, 也就是所有边容量的最小值, 然后更新所有边的容量

E d m o n d s – K a r p Edmonds–Karp EdmondsKarp算法求最大流

时间复杂度 O ( n m 2 ) O(nm ^ 2) O(nm2)

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1010, M = 20010, INF = 0x3f3f3f3f;

int n, m, s_node, t_node;
int head[N], edge_end[M], next_edge[M], w[M], edge_index;
int q[N], pre[N], min_val[N];
bool vis[N];

void add(int ver1, int ver2, int val) {
    edge_end[edge_index] = ver2, next_edge[edge_index] = head[ver1], w[edge_index] = val, head[ver1] = edge_index++;
}

bool bfs() {
    memset(vis, false, sizeof vis);

    int h = 0, t = -1;
    q[++t] = s_node;
    vis[s_node] = true;
    min_val[s_node] = INF;

    while (h <= t) {
        int u = q[h++];
        for (int i = head[u]; ~i; i = next_edge[i]) {
            int ver = edge_end[i];
            if (!vis[ver] && w[i]) {
                vis[ver] = true;
                min_val[ver] = min(min_val[u], w[i]);
                pre[ver] = i;
                if (ver == t_node) return true;
                q[++t] = ver;
            }
        }
    }

    return false;
}

int edmonds_karp() {
    int res = 0;

    while (bfs()) {
        int val = min_val[t_node];
        res += val;

        for (int i = t_node; i != s_node; i = edge_end[pre[i] ^ 1]) {
            w[pre[i]] -= val;
            w[pre[i] ^ 1] += val;
        }
    }

    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    memset(head, -1, sizeof head);
    cin >> n >> m >> s_node >> t_node;

    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, 0);
    }

    int res = edmonds_karp();

    cout << res << "\n";
    return 0;
}


D i n i c Dinic Dinic算法求最大流

将所有能够增广的路径全部计算, 因为可能有环, 因此使用分层图优化, 路径只能从前一层走到后一层
分层图 + 当前弧优化
时间复杂度 O ( n 2 m ) O(n ^ 2m) O(n2m)

在这里插入图片描述
从起点到终点, 可以流 l i m i t limit limit的流量, 搜索从当前点开始到终点能流的流量

在这里插入图片描述
假设在搜索到终点后流量 f i < l i m i t f_i < limit fi<limit, 说明 f i f_i fi一定会满流, 那么在下一次搜索的时候不需要搜索该边

在这里插入图片描述
而是从下一条边开始搜, 代码表示为 c u r r [ u ] = i curr[u] = i curr[u]=i

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 10010, M = 200010, INF = 0x3f3f3f3f;

int n, m, s_node, e_node;
int head[N], edge_end[M], next_edge[M], w[M], edge_index;
int q[N], layer[N], curr[N];

void add(int ver1, int ver2, int val) {
	edge_end[edge_index] = ver2, next_edge[edge_index] = head[ver1], w[edge_index] = val, head[ver1] = edge_index++;
}

bool bfs() {
	memset(layer, -1, sizeof layer);

	int h = 0, t = -1;
	q[++t] = s_node;
	layer[s_node] = 0;
	curr[s_node] = head[s_node];

	while (h <= t) {
		int u = q[h++];

		for (int i = head[u]; ~i; i = next_edge[i]) {
			int ver = edge_end[i];

			if (layer[ver] == -1 && w[i]) {
				layer[ver] = layer[u] + 1;
				curr[ver] = head[ver];
				if (ver == e_node) return true;
				q[++t] = ver;
			}
		}
	}

	return false;
}

int dfs(int u, int limit) {
	if (u == e_node) return limit;

	// 从当前点向后流的流量
	int flow = 0;
	for (int i = curr[u]; ~i && flow < limit; i = next_edge[i]) {
		int ver = edge_end[i];

		// i前面的边都用完了, 当前弧更新为i
		curr[u] = i;
		if (layer[ver] == layer[u] + 1 && w[i]) {
			int val = dfs(ver, min(w[i], limit - flow));
			if (!val) layer[ver] = -1;

			w[i] -= val;
			w[i ^ 1] += val;
			flow += val;
		}
	}
	return flow;
}

int dinic() {
	int res = 0, flow;
	while (bfs()) {
		// 搜索增广路径并且累计全部的流量
		while ((flow = dfs(s_node, INF))) {
			res += flow;
		}
	}

	return res;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	memset(head, -1, sizeof head);
	cin >> n >> m >> s_node >> e_node;
	while (m--) {
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w);
		add(v, u, 0);
	}

	int res = dinic();
	cout << res << "\n";

	return 0;
}

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

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

相关文章

SpringBoot对接DeepSeek

文章目录 Spring Boot 集成 DeepSeek API 详细步骤1. 创建API Key1.访问 [DeepSeek控制台](https://platform.deepseek.com/usage) 并登录。2.点击 Create API Key 生成新密钥。3.复制并保存密钥&#xff08;需在Spring Boot配置文件中使用&#xff09;。 2. 创建Spring Boot工…

大语言模型的多垂类快速评估与 A/B 测试

简介 行业领先的模型构建企业携手澳鹏&#xff08;Appen&#xff09;开展了一项极具挑战性的项目。针对 3 至 6 个大型语言模型&#xff08;LLM&#xff09;&#xff0c;在广泛的通用领域及复杂专业领域&#xff08;如医疗保健、法律、金融、编程、数学和汽车行业等&#xff0…

RAGFlow + LlamaIndex 本地知识库RAG增强架构与实现直播智能复盘

一、需求分析与架构设计 基于 RAGFlow LlamaIndex 本地知识库RAG 扩展直播话术合规与复盘系统&#xff0c;需构建 实时流处理、多模态合规引擎、智能复盘分析 三层能力。以下是完整架构图与技术方案&#xff1a; 二、核心模块技术方案 1. 直播流实时处理&#xff08;输入层→…

阿里云平台服务器操作以及发布静态项目

目录&#xff1a; 1、云服务器介绍2、云服务器界面3、发布静态项目1、启动nginx2、ngixn访问3、外网访问测试4、拷贝静态资源到nginx目录下并重启nginx 1、云服务器介绍 2、云服务器界面 实例详情&#xff1a;里面主要显示云服务的内外网地址以及一些启动/停止的操作。监控&…

【大模型实战篇】使用GPTQ量化QwQ-32B微调后的推理模型

1. 量化背景 之所以做量化&#xff0c;就是希望在现有的硬件条件下&#xff0c;提升性能。量化能将模型权重从高精度&#xff08;如FP32&#xff09;转换为低精度&#xff08;如INT8/FP16&#xff09;&#xff0c;内存占用可减少50%~75%。低精度运算&#xff08;如INT8&#xf…

基于springboot医疗平台系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 信息化时代&#xff0c;各行各业都以网络为基础飞速发展&#xff0c;而医疗服务行业的发展却进展缓慢&#xff0c;传统的医疗服务行业已经逐渐不满足民众的需求&#xff0c;有些还在以线下预约挂号的方式接待病人&#xff0c;为此设计一个医疗平台系统很有必要。此类系统…

Stable Diffusion lora训练(一)

一、不同维度的LoRA训练步数建议 2D风格训练 数据规模&#xff1a;建议20-50张高质量图片&#xff08;分辨率≥10241024&#xff09;&#xff0c;覆盖多角度、多表情的平面风格。步数范围&#xff1a;总步数控制在1000-2000步&#xff0c;公式为 总步数 Repeat Image Epoch …

网络空间安全(37)获取webshell方法总结

一、直接上传获取Webshell 这是最常见且直接的方法&#xff0c;利用网站对上传文件的过滤不严或存在漏洞&#xff0c;直接上传Webshell文件。 常见场景&#xff1a; 许多PHP和JSP程序存在此类漏洞。例如&#xff0c;一些论坛系统允许用户上传头像或心情图标&#xff0c;攻击者可…

第十三次CCF-CSP认证(含C++源码)

第十三次CCF-CSP认证 跳一跳满分题解 碰撞的小球满分题解遇到的问题 棋局评估满分题解 跳一跳 题目链接 满分题解 没什么好说的 基本思路就是如何用代码翻译题目所给的一些限制&#xff0c;以及变量应该如何更新&#xff0c;没像往常一样给一个n&#xff0c;怎么读入数据&…

swagger ui 界面清除登录信息的办法

我们在开发过程中&#xff0c;用swagger ui 测试接口的时候&#xff0c;可能会要修改当前登录的用户。 但是如果我们在谷歌中对调试的本地swagger ui 登录地址存储过账户密码&#xff0c;每次启动项目调试之后&#xff0c;都会自动登录swagger ui &#xff0c;登录界面一闪就…

TensorFlow 的基本概念和使用场景

TensorFlow 是一个由 Google 开发的开源机器学习框架&#xff0c;主要用于构建和训练深度学习模型。下面是一些 TensorFlow 的基本概念和使用场景&#xff1a; 基本概念&#xff1a; 张量&#xff08;Tensor&#xff09;&#xff1a;在 TensorFlow 中&#xff0c;数据以张量的…

基于x11vnc的ubuntu远程桌面

1、安装VNC服务 sudo apt install x11vnc -y2、创建连接密码 sudo x11vnc -storepasswd3、安装lightdm服务 x11vnc 在 默认的 GDM3 中不起作用&#xff0c;因此需要使用 lightdm 桌面管理环境 sudo apt install lightdm -y切换至lightdm&#xff0c;上一步已经切换则跳过该…

Cursor解锁Claude Max,助力AI编程新突破!

Cursor 最新推出的 Claude Max 模型&#xff0c;以其卓越的性能和创新的能力&#xff0c;正在重新定义我们对 AI 辅助编程的认知。这款搭载 Claude3.7 大脑的超级模型&#xff0c;不仅具备超强智能&#xff0c;还凭借一系列技术突破&#xff0c;向传统 AI 编程工具发起了挑战。…

ESP8266 与 ARM7 接口-LPC2148 创建 Web 服务器以控制 LED

ESP8266 与 ARM7 接口-LPC2148 创建 Web 服务器以控制 LED ESP8266 Wi-Fi 收发器提供了一种将微控制器连接到网络的方法。它被广泛用于物联网项目,因为它便宜、体积小且易于使用。 在本教程中,我们将 ESP8266 Wi-Fi 模块与 ARM7-LPC2148 微控制器连接,并创建一个 Web 服务…

通过C#脚本更改材质球的参数

// 设置贴图Texture mTexture Resources.Load("myTexture", typeof(Texture )) as Texture;material.SetTexture("_MainTex", mTexture );// 设置整数material.SetInt("_Int", 1);// 设置浮点material.SetFloat("_Float", 0.1f);// 设…

FPGA管脚约束

目录 前言 一、IO约束 二、延迟约束 前言 IO约束包括管脚约束和延迟约束。 一、IO约束 对管脚进行约束&#xff0c;对应的约束语句&#xff1a; set_property -dict {PACKAGE_PIN AJ16 IOSTANDARD LVCMOS18} [get_ports "led[0]" ] 上面是单端的管脚&…

实现前端.ttf字体包的压缩

前言 平常字体包都有1M的大小&#xff0c;所以网络请求耗时会比较长&#xff0c;所以对字体包的压缩也是前端优化的一个点。但是前端如果想要特点字符打包成字体包&#xff0c;网上查阅资料后&#xff0c;都是把前端代码里面的字符获取&#xff0c;但是对于动态的内容&#xf…

uni-app集成保利威直播、点播SDK经验FQ(二)|小程序直播/APP直播开发适用

通过uniapp集成保利威直播、点播SDK来开发小程序/APP的视频直播能力&#xff0c;在实际开发中可能会遇到的疑问和解决方案&#xff0c;下篇。更多疑问请咨询19924784795。 1.ios不能后台挂起uniapp插件 ios端使用后台音频播放和画中画功能&#xff0c;没有在 manifest.json 进…

Sensodrive机器人力控关节模组SensoJoint在海洋垃圾清理机器人中的拓展应用

海洋污染已成为全球性的环境挑战&#xff0c;其中海底垃圾的清理尤为困难。据研究&#xff0c;海洋中约有2600万至6600万吨垃圾&#xff0c;超过90%沉积在海底。传统上&#xff0c;潜水员收集海底垃圾不仅成本高昂&#xff0c;而且充满风险。为解决这一问题&#xff0c;欧盟资助…

Git的基本指令

一、回滚 1.git init 在项目文件夹中打开bash生成一个.git的子目录&#xff0c;产生一个仓库 2.git status 查看当前目录下的所有文件的状态 3.git add . 将该目录下的所有文件提交到暂存区 4.git add 文件名 将该目录下的指定文件提交到暂存区 5.git commit -m 备注信…