网络流的认识

网络流的认识

什么是流网络

网络(network)是指一个特殊的有向图  G = ( V , E ) G = (V,E) G=(V,E),其与一般有向图的不同之处在于有容量和源汇点,不考虑反向边。
其中,我们有以下变量来方便表示:

  • S S S:源点
  • T T T:汇点
  • c ( u , v ) c(u,v) c(u,v):表示从 u u u v v v 这条有向边的容量 c ( u , v ) c(u,v) c(u,v).
  • f ( u , v ) f(u,v) f(u,v):表示从 u u u v v v 这条有向边的流量 f ( u , v ) f(u,v) f(u,v).

没有网络连接到洛谷图床

图  1 \text{图 } 1  1
如上图,这就是一个流网络,其中 c ( 1 , 3 ) = 4 c(1,3)=4 c(1,3)=4 表示的就是 1 1 1 3 3 3 的容量为 4 4 4,源点 S S S 往汇点 T T T 进行流水操作,其中 S S S T T T 能容下水的量是无限量的。(这里的水只是打个比方,因为很像自来水工厂为我们供水)。

什么是可行流

可行流,通俗点讲,就是在每条变分配流水的多少,使能满足条件(这个在生活实际也能推出)。
条件显然为以下:

  • 流量限制: 0 ≤ f ( u , v ) ≤ c ( u , v ) 0\leq f(u,v)\leq c(u,v) 0f(u,v)c(u,v),你这条水管的流量如果大于容量,后果不堪设想。
  • 流量守恒:抽象点讲,也就是你当前的点为 u u u,入点为 p 1 , p 2 , … , p k 1 p_1,p_2,\dots,p_{k1} p1,p2,,pk1,出点分别为 q 1 , q 2 , … , q k 2 q_1,q_2,\dots,q_{k2} q1,q2,,qk2,那么显然:
    ∑ i = 1 i ≤ k 1 f ( p i , u ) = ∑ i = 1 i ≤ k 2 f ( u , q i ) \sum_{i=1}^{i\leq k1}f(p_i,u)=\sum_{i=1}^{i\leq k2}f(u,q_i) i=1ik1f(pi,u)=i=1ik2f(u,qi)
    形象点讲,也就是我当前流入到这个点的水的量最终都会流出到我可以到的点。

我们用 f f f 表示一个可行流,如下图:

有网络连接到洛谷图床
图  2 \text{图 } 2  2

其中在 1 1 1 3 3 3 这条边上, f ( 1 , 3 ) = 2 f(1,3) = 2 f(1,3)=2 c ( 1 , 3 ) = 4 c(1,3) = 4 c(1,3)=4,显然满足条件: 0 ≤ f ( 1 , 3 ) = 2 ≤ 4 = c ( 1 , 3 ) 0\leq f(1,3) = 2\leq 4 = c(1,3) 0f(1,3)=24=c(1,3).
你可以试试看,能否满足第二个条件?

什么是最大流

最大流(也称最大可行流)实际是在可行流中找一个方案,使得流入汇点 T T T 的水的量最大。我们用 ∣ f ∣ |f| f 表示 S S S T T T 点流量总和。根据定义显然有下种公式:
∣ f ∣ = ∑ ( u , v ) ∈ E f ( u , v ) − ∑ ( u , v ) ∈ E f ( v , u ) . |f| = \sum_{(u,v)\in E} f(u,v) - \sum_{(u,v)\in E} f(v,u). f=(u,v)Ef(u,v)(u,v)Ef(v,u).
在这里,右边的式子的第二个单项式 ∑ ( u , v ) ∈ E f ( v , u ) \sum_{(u,v)\in E} f(v,u) (u,v)Ef(v,u) 可以忽略不计,为了严谨,考虑了反向边的情况。

What is 残留网络

概念和构建

残留网络总是针对原图 G = ( V , E ) G =(V,E) G=(V,E) 中的某一个可行流而言,因此,可以将残留网络看成是可行流的一个函数,通常记为 G f G_f Gf.
G f = ( V f , E f ) G_f = (V_f,E_f) Gf=(Vf,Ef),其中 V f = V V_f =V Vf=V E f = E E_f = E Ef=E E E E 中所有的反向边。
残留网络中的容量记为 c ′ ( u , v ) c'(u,v) c(u,v),且 ( u , v ) ∈ E , ( v , u ) ∈ E (u,v) \in E,(v,u) \in E (u,v)E,(v,u)E,定义为:
c ′ ( u , v ) = { c ( u , v ) − f ( u , v ) ( u , v ) ∈ E f ( v , u ) ( u , v ) ∈ E c'(u,v) = \left\{\begin{matrix} c(u,v) - f(u,v)\quad (u,v)\in E\\ f(v,u) \quad (u,v)\in E \end{matrix}\right. c(u,v)={c(u,v)f(u,v)(u,v)Ef(v,u)(u,v)E

作用

可以通过 增广路径 的配合找到更大的流,使最后图中的最大流最大。

增广路径

定义

如果从源点 S S S 出发沿着残留网络中容量大于 0 0 0 的边走,可以走到汇点 T T T,那么将走过的边所组成的路径称为增广路径。
在这里我们发现:原网络可行流+残留网络可行流也是原网络的一个可行流
抽象点说(正式点说), f + f ′ f+f' f+f 属于 G G G 的一个可行流,且有:
∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ |f + f'| = |f| + |f'| f+f=f+f

在网络中定点的一个划分,把所有顶点分成两个集合 S S S T T T,其中 S ∈ S , T ∈ T S\in S,T\in T SS,TT,而且有 S ∪ T = V , S ∩ T = ∅ S\cup T=V,S\cap T=\emptyset ST=V,ST=,记为 [ S , T ] [S,T] [S,T].

割的容量

指连接两个点集的边的容量总和,即 c ( S , T ) = ∑ u ∈ S ∑ v ∈ T c ( u , v ) c(S,T)=\sum_{u\in S}\sum_{v\in T}c(u,v) c(S,T)=uSvTc(u,v)

割的流量

指指连接两个点集的边的流量总和,
由上同理可得:
f ( S , T ) = ∑ u ∈ S ∑ v ∈ T ( c ( u , v ) − c ( v , u ) ) f(S,T) = \sum_{u\in S}\sum_{v\in T}(c(u,v)-c(v,u)) f(S,T)=uSvT(c(u,v)c(v,u))
有反向边时, c ( v , u ) c(v,u) c(v,u) 才有确值。
显然:
0 ≤ f ( S , T ) ≤ c ( S , T ) 0\leq f(S,T)\leq c(S,T) 0f(S,T)c(S,T)

最小割

G G G 中所有割组成的集合中,容量最小的元素。

最大流最小割定理

以下 3 3 3 个,知 1 1 1 2 2 2

  • 1 1 1 f f f 是最大流
  • 2 2 2 G f G_f Gf 不存在增广路
  • 3 3 3 ∃ [ S , T ] ∃[S,T] [S,T],满足 ∣ f ∣ = c ( S , T ) |f|=c(S,T) f=c(S,T) ∃ ∃ 表示存在一个)。

证明:

  • 证明 1 → 2 1\rightarrow 2 12
    反证即可,若存在增广路就会使得当前的 f f f 不是最大流,也就是 ∣ f ∣ + ∣ f ′ ∣ > ∣ f ∣ |f| + |f'|>|f| f+f>f,由条件又可知道: ∣ f ∣ |f| f 最大,所以说当 G f G_f Gf 不存在增广路时, f f f 为最大流。
  • 证明 2 → 3 2\rightarrow 3 23
    我们将对于 G f G_f Gf 中从 S S S 出发沿着容量大于 0 0 0 的边可以到达的点全部放入集合 S S S 中,然后令 T = V − S T = V - S T=VS,那么对于点 x ∈ S , y ∈ Y x\in S,y\in Y xS,yY,边 ( x , y ) (x,y) (x,y) 必有 f ( x , y ) = c ( x , y ) f(x,y) = c(x,y) f(x,y)=c(x,y)
  • 证明 3 → 1 3\rightarrow 1 31
    因为 ∣ f ∣ ≤ 最大流 ≤ c ( S , T ) |f|\leq \text{最大流}\leq c(S,T) f最大流c(S,T),而由 3 3 3 可知 ∣ f ∣ = c ( S , T ) |f|=c(S,T) f=c(S,T),故上式取等,即 f f f 是最大流。

求最大流

基于上述定理,我们可以不断寻找增广路,利用增广路更新残留网络,直到找不到增广路,即可求得最大流。

EK 算法

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

改图
继续寻找
拿到原图
残留网络
找增广路
是否存在增广路到达 T
更新残留网络
找到最大流
输出
Code1
#include<iostream>
#include<cstring>
using namespace std;

const int INF=1e9;
const int N=1005, M=10010;
int n, m, S, T;
struct node{
	int to, c, next;
}e[M<<1];
int h[N], tot;

// 残量网络建图,初始时正向的容量是 c, 反向容量是 0 。
void add(int u, int v, int c){
	e[tot].to=v, e[tot].c=c, e[tot].next=h[u], h[u]=tot++;
	e[tot].to=u, e[tot].c=0, e[tot].next=h[v], h[v]=tot++;	
}

int lim[N], pre[N]; // lim[u] 表示 S 到点 u 路径容量的最小值, pre[u] 表示 u 的前驱边。
bool vis[N];
int q[N];

// bfs 找增广路。
bool bfs(){
	memset(vis, false, sizeof vis);
	int hh=0, tt=-1;
	q[++tt]=S, vis[S]=true, lim[S]=INF;
	
	while(tt>=hh){
		int hd=q[hh++];
		for(int i=h[hd]; ~i; i=e[i].next){
			int go=e[i].to;
			if(vis[go] || !e[i].c) continue;
			vis[go]=true, q[++tt]=go;
			lim[go]=min(lim[hd], e[i].c);
			pre[go]=i;
			if(go==T) return true;
		}
	}
	return false;
}

int EK(){
	int res=0;
	while(bfs()){
		res+=lim[T];
		for(int i=T; i!=S; i=e[pre[i]^1].to){
			e[pre[i]].c-=lim[T], e[pre[i]^1].c+=lim[T];
		}
	}
	return res;
}
int main(){
	memset(h, -1, sizeof h);
	cin>>n>>m>>S>>T;
	while(m--){
		int u, v, c; cin>>u>>v>>c;
		add(u, v, c);
	}	
	cout<<EK()<<endl;
	return 0;
}
Code2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstring>
#define N 1007
#define M 10007
#define int long long
using namespace std;
const int INF = 1e9 + 7;
struct node{
	int to,val,nxt;
}e[M << 1];
int n, m, S, T, tot;
int head[N],dis[N],pre[N];
bool vis[N];
queue<int> q;
void add(int a,int b,int c) {
	e[tot].to = b, e[tot].val = c, e[tot].nxt = head[a], head[a] = tot++;
	e[tot].to = a, e[tot].val = 0, e[tot].nxt = head[b], head[b] = tot++;
}
bool bfs() {
	while(!q.empty()) q.pop();
	memset(vis,false,sizeof vis);
	q.push(S), vis[S] = true, dis[S] = INF;
	while(!q.empty()) {
		int t = q.front();
		q.pop();
		for (int i = head[t];i != -1;i = e[i].nxt) {
			int v = e[i].to;
			if (!vis[v] && e[i].val) {
				vis[v] = true;
				dis[v] = min(dis[t],e[i].val);
				pre[v] = i;
				if (v == T) return true;
				q.push(v);
			}
		}
	}
	return false;
}
int EK(){
	int r = 0;
	while(bfs()) {
		r += dis[T];
		for (int i = T;i != S;i = e[pre[i] ^ 1].to)
			e[pre[i]].val -= dis[T], e[pre[i] ^ 1].val += dis[T];
	}
	return r;
}

signed main(){
	cin >>n >> m >> S>> T;
	memset(head, -1,sizeof head);
	for(;m--;) {
		int a,b,c;
		cin >> a >> b >> c;
		add(a,b,c);
	}
	cout << EK();
	return 0;
}

Dinic 算法

多路增广,时间复杂度 O ( n 2 m ) O(n^2m) O(n2m)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define gc() (st==ed&&(ed=(st=buf)+fread(buf,1,100000,stdin),st==ed)?EOF:*st++)
char buf[100001],*st=buf,*ed=buf;
void read(int &a){
    a=0;char c=gc();
    while(c>'9'||c<'0')c=gc();
    while(c>='0'&&c<='9')a=a*10+c-48,c=gc();
}

const int INF=0x3f3f3f3f;
const int N=10010, M=1e5+5;

struct node{
    int to, c, next;
}e[M<<1];
int h[N], tot;

void add(int u, int v, int cap){
    e[tot].to=v, e[tot].c=cap, e[tot].next=h[u], h[u]=tot++;
    e[tot].to=u, e[tot].c=0, e[tot].next=h[v], h[v]=tot++;  
}

int n, m, S, T;

int d[N], q[N], cur[N];

bool bfs(){
    memset(d, -1, sizeof d);
    int tt=-1, hh=0;
    q[++tt]=S, d[S]=0, cur[S]=h[S];

    while(tt>=hh){
        int hd=q[hh++];
        for(int i=h[hd]; ~i; i=e[i].next){
            int go=e[i].to;
            if(d[go]==-1 && e[i].c){
                d[go]=d[hd]+1;
                cur[go]=h[go];
                if(go==T) return true;
                q[++tt]=go;
            }
        }
    }
    return false;
}

int find(int u, int limit){
    if(u==T) return limit;
    int flow=0;
    for(int i=cur[u]; ~i && flow<limit; i=e[i].next){
        cur[u]=i;
        int go=e[i].to;
        if(d[go]==d[u]+1 && e[i].c){
            int t=find(go, min(e[i].c, limit-flow));
            if(!t) d[go]=-1;
            e[i].c-=t, e[i^1].c+=t, flow+=t;
        }
    }
    return flow;
}

int dinic(){
    int res=0, flow;
    while(bfs()) while(flow=find(S, INF)) res+=flow;
    return res;
}

signed main(){
    memset(h, -1, sizeof h);
    read(n), read(m), read(S), read(T);
    while(m--){
        int u, v, cap; read(u), read(v), read(cap);
        add(u, v, cap);
    }

    cout<<dinic()<<endl;

    return 0;
}

完结。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024美赛C题保姆级分析完整思路代码数据教学

2024美国大学生数学建模竞赛C题保姆级分析完整思路代码数据教学 C题 Momentum in Tennis 网球中的动量 在2023年温布尔登男单决赛中&#xff0c;20岁的西班牙新星卡洛斯阿尔卡拉兹击败了36岁的诺瓦克德约科维奇。这是德约科维奇自2013年以来在温布尔登的首次失利&#xff0c;也…

SwiftUI 动画入门之二:几何特效动画(GeometryEffect)

概览 在上一篇博文 SwiftUI 动画入门之一:路径动画(Path Animations)中,我们讨论了如何打造折线图(LinesGrap)形状上的路径动画。 而在本篇博文中,我们在前篇实现基础之上通过 GeometryEffect 特效为任意路径动画加上了活泼可爱的“小尾巴”。这是怎么做到的呢? 在本…

格式化内存卡后,如何找回丢失的监控视频?

随着摄像头的应用越来越广泛&#xff0c;很多监控摄像头采用了内存卡作为存储介质&#xff0c;方便用户存储和查看摄像头拍摄的视频文件。然而&#xff0c;由于各种原因&#xff0c;监控摄像头的内存卡有时会被意外格式化导致重要数据的丢失&#xff0c;给用户带来诸多困扰。 那…

有色金属矿山采选智能工厂数字孪生可视化,推进矿采选业数字化转型

有色金属矿山采选智能工厂数字孪生可视化&#xff0c;推进矿采选业数字化转型。随着科技的不断发展&#xff0c;数字化转型已经成为各行各业发展的必然趋势。有色金属矿采选业作为传统工业的重要组成部分&#xff0c;也面临着数字化转型的挑战。为了更好地推进有色金属矿采选业…

C语言字符、字符串

一、c语言字符串的本质 1、char类型数组 c语言没有专门用来存储字符串的变量类型&#xff0c;字符串都是存储在char类型的数组中&#xff0c;char类型的连续空间中每个存储单元存储一个字符&#xff0c;数组末尾以’\0’结束&#xff0c;标志字符串的结束。\0’是空字符&…

开源编辑器:ONLYOFFICE文档又更新了!

办公软件 ONLYOFFICE文档最新版本 8.0 现已发布&#xff1a;PDF 表单、RTL、单变量求解、图表向导、插件界面设计等更新。 什么是 ONLYOFFICE 文档 ONLYOFFICE 文档是一套功能强大的文档编辑器&#xff0c;支持编辑处理文本文档、电子表格、演示文稿、可填写的表单、PDF&#…

大语言模型之LlaMA系列- LlaMA 2及LLaMA2_chat(上)

LlaMA 2是一个经过预训练与微调的基于自回归的transformer的LLMs&#xff0c;参数从7B至70B。同期推出的Llama 2-Chat是Llama 2专门为对话领域微调的模型。 在许多开放的基准测试中Llama 2-Chat优于其他开源的聊天模型&#xff0c;此外Llama 2-Chat还做了可用性与安全性评估。 …

IP定位如何进行业务风控反欺诈

IP地址作为接入互联网的唯一标识&#xff0c;分析其归属地及网络类型等多维度信息&#xff0c;帮助识别虚假流量和欺诈账号&#xff0c;保障账号和交易安全&#xff0c;帮助企业持续优化风控与反欺诈模型&#xff0c;降低经济损失。 交易聚集分析 通过IP地址数据服务得到的交易…

Pytorch从零开始实战18

Pytorch从零开始实战——人脸图像生成 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——人脸图像生成环境准备模型定义开始训练可视化总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;Pytorch2.0.1cu118&#…

Linux下gcc的使用与程序的翻译

gcc和程序的翻译过程 gcc介绍程序的翻译过程预编译编译汇编链接 命令行式宏定义 gcc介绍 gcc是一款编译C语言编译器&#xff0c;可以把我们用vim写的代码编译成可执行程序。编译C用g进行编译&#xff0c;C的文件后缀是test.cc或test.cpp或test.cxx 如果要安装g就执行以下命令 …

一文详解docker swarm

文章目录 1、简介1.1、涉及到哪些概念&#xff1f;1.2、需要注意什么&#xff1f; 2、集群管理2.1、创建集群2.2、将节点加入集群2.3、查看集群状态。2.4、将节点从集群中移除2.5、更新集群2.6、锁定/解锁集群 3、节点管理4、服务部署4.1、准备4.2、服务管理4.2.1、常用命令4.2…

TCP 连接掉线自动重连

文章目录 TCP 连接掉线自动重连定义使用连接效果 TCP 接收数据时防止掉线。TCP 连接掉线自动重连。多线程环境下TCP掉线自动重连。 欢迎讨论更好的方法&#xff01; TCP 连接掉线自动重连 定义 定义一个类&#xff0c;以编写TCP连接函数Connect()&#xff0c;并且&#xff1a…

分发糖果[困难]

优质博文&#xff1a;IT-BLOG-CN 一、题目 n个孩子站成一排。给你一个整数数组ratings表示每个孩子的评分。你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 【1】每个孩子至少分配到1个糖果。 【2】相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩…

JavaScript基础五对象 内置对象 Math.random()

内置对象-生成任意范围随机数 Math.random() 随机数函数&#xff0c; 返回一个0 - 1之间&#xff0c;并且包括0不包括1的随机小数 [0, 1&#xff09; 如何生成0-10的随机数呢&#xff1f; Math.floor(Math.random() * (10 1)) 放大11倍再向下取整 如何生成5-10的随机数&…

【智能算法】11种混沌映射算法+2种智能算法示范【鲸鱼WOA、灰狼GWO算法】

1 主要内容 混沌映射算法是我们在智能算法改进中常用到的方法&#xff0c;本程序充分考虑改进算法应用的便捷性&#xff0c;集成了11种混合映射算法&#xff0c;包括Singer、tent、Logistic、Cubic、chebyshev、Piecewise、sinusoidal、Sine、ICMIC、Circle、Bernoulli&#xf…

css实现按钮边框旋转

先上效果图 本质&#xff1a;一个矩形在两个矩形互相重叠遮盖形成的缝隙中旋转形成&#xff0c;注意css属性z-index层级关系 直接上代码 <div class"bg"><div class"button">按钮</div></div><style>.bg {width: 100%;heigh…

数字图像处理(实践篇)四十一 OpenCV-Python 使用sift算法检测图像上的特征点实践

目录 一 涉及的函数 二 实践 2004年,D.Lowe在论文Distinctive Image Features from Scale-Invariant Keypoints中提出了一种新算法,即尺度不变特征变换 (SIFT),该算法提取关键点并计算其描述符。SIFT提取图像的局部特征,在尺度空间寻找极值点,并提取出其位置尺度和方向…

绝地求生:“龙腾”通行证和新空投任务内容一览:二十级依然有图纸!

大家好&#xff0c;27.2版本终于更新完了&#xff0c;先为大家带来这次龙腾通行证的详细内容&#xff0c;显放上详细的兑换点数大家可以慢慢看。 省流: 通行证分支3仍然可解锁图纸和500G-COIN奖励&#xff0c;空投任务也可以通过做很简单的游戏任务70代币兑换获得1张图纸。 这次…

main函数中参数argc和argv用法解析

1 基础 argc 是 argument count 的缩写&#xff0c;表示传入main函数的参数个数&#xff1b; argv 是 argument vector 的缩写&#xff0c;表示传入main函数的参数序列或指针&#xff0c;并且第一个参数argv[0]一定是程序的名称&#xff0c;并且包含了程序所在的完整路径&…

vue 发布自己的npm组件

1、在项目任意位置创建index.ts文件 2、导入要到处的组件&#xff0c;使用vue提供的install 功能全局挂在&#xff1b; import GWButton from "/views/GWButton.vue"; import GWAbout from "/views/AboutView.vue";const components {GWButton,GWAbout, …