网络流初步

news/2025/1/4 14:45:47/文章来源:https://www.cnblogs.com/qc0817/p/18646224

网络流初步(脑部整理)

呜呜呜,家人们也是学上网络流了。

咸鱼起手,你反应得过来吗?

英语不太好(老英不会看窝博客吧)

What is 网络流?

概述

网络\((network)\)是指一个特殊的有向图 \(G=(V,E)\),其与一般有向图的不同之处在于有容量和源汇点。

  • $E $中的每条边 $ (u, v)$ 都有一个被称为容量 \((capacity)\)的权值,记作 $ c(u, v)$。当 \((u,v)\)
    $\notin E $时,可以假定 \(c(u,v)=0\)
  • $V $ 中有两个特殊的点:源点 \((source)s\) 和汇点 \((sink)t(s \neq t)\)

对于网络 $ G=(V, E)$,流 \((flow)\) 是一个从边集 E 到整数集或实数集的函数,其满足以下性质。

  • 容量限制:对于每条边,流经边的流量不得超过该边的容量,即 $ 0 \leq f(u,v) \leq c(u,v)$ ;
  • 流守恒性:除源汇点外,任意结点$ u$ 的净流量为$ 0$。

其中,我们定义 $u $的净量为 \(f(u) = \sum_{x \in V} f(u, x) - \sum_{x \in V} f(x, u)\)。对于网络$ G = (V, E) $和其上的流 \(f\),我们定义 $ f$ 的流量 $ |f| $为
$s $的净流量 $ f(s)$。作为流守恒性的推论,这也等于 $ t$ 的净流量的相反数 \(-f(t)\)。对于网络 \(G = (V, E)\),如果 ${S, T} $是 \(V\) 的划分(即 \(S \cup T = V 且 S \cap T = \varnothing\)),且满足$ s \in S, t \in T$,则我们称 \(\{S, T\}\)\(G\) 的一个 \(s-t\) 割(cut)。我们定义 \(s-t\) 割$ {S, T}$ 的容量为 \(||S, T|| = \sum_{u \in S} \sum_{v \in T} c(u, v)\)

--以上摘自\(OI-Wiki\)

靠! 什么玩意?都是中文咋看不懂?

于是我们找到了启蒙例(ti)题(jie)。然后偷税得发现,其实就是一堆的水管,每个水管有相应的容量\((c)\),并且水管里的水只能单向流动,接着让我处理一些有关流量\((f)\)头大有趣问题。

可以发现,对于一张网络,有这么几个重点。

  • 容量限制:$f(u,v) \leq c(u,v) $。一条边上的流量不能大于水管容量(不然就要爆炸)。
  • 相反净流量:\(f(u,v)=-f(v,u)。\)\(u\)\(v\)的净流量必须是\(v\)\(u\)净流量的相反数。
  • 流守恒性:除非\(u=s\)\(u=t\),否则\(\sum_{x \in V} f(u, x)=0\)。一个非源非汇的点的净流量一定是\(0\)

最大流

根据名字显然可以知道最大流就是最大流量的意思

其实就是以下意思:

【模板】网络最大流

题目描述

如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。

输入格式

第一行包含四个正整数 \(n,m,s,t\),分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来 \(m\) 行每行包含三个正整数 \(u_i,v_i,w_i\),表示第 \(i\) 条有向边从 \(u_i\) 出发,到达 \(v_i\),边权为 \(w_i\)(即该边最大流量为 \(w_i\))。

输出格式

一行,包含一个正整数,即为该网络的最大流。

样例 #1

样例输入 #1

4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 30

样例输出 #1

50

提示

样例输入输出 1 解释

题目中存在 \(3\) 条路径:

  • \(4\to 2\to 3\),该路线可通过 \(20\) 的流量。
  • \(4\to 3\),可通过 \(20\) 的流量。
  • \(4\to 2\to 1\to 3\),可通过 \(10\) 的流量(边 \(4\to 2\) 之前已经耗费了 \(20\) 的流量)。

故流量总计 \(20+20+10=50\)。输出 \(50\)


数据规模与约定

  • 对于 \(30\%\) 的数据,保证 \(n\leq10\)\(m\leq25\)
  • 对于 \(100\%\) 的数据,保证 \(1 \leq n\leq200\)\(1 \leq m\leq 5000\)\(0 \leq w\lt 2^{31}\)

青铜 — — Ford-Fulkerson算法(增广路算法)

首先我们先找到一条从\(s\)\(t\)容量不为零的路径,就是传说中的增广路,然后把一条条增广路加在一起,似乎就可以得到一个答案。

但如果选择的路径较劣就会影响最后的答案。

那怎么办呢?增广路真的一点鸟用都没有吗?

当然,部分人类凭借高级的大脑想出了处理方法——建一条反方向的边。正向边减少的同时给反向边进行增加,即利用\(f(u,v)=-f(u,v)\)这样就可以实现类似于反悔贪心的操作,至于正确性。先引入一个叫做残量网络的概念。可以理解为把满流的边扔掉后,边权为剩余流量的图。(包括反向边)。当在残量网络中流过一条反向边时,相当于将原先的流压了回去,而通过列举简单的例子,可以发现流经反向边事实上是一个反悔的等价操作。

每次只需要在残量网络中找增广路即可。

局限性

但这样跑肥肠靠RP,只需要简单造造数据就可以把算法卡成 \(O(值域)\) 的,显然是指数级算法。

那这算法有个蛋用啊?表演T飞吗?但这个算法可以打暴力通过合理优化达到高效的运行。但增广路算法是基础。

Edmonds-Karp算法(EK算法)

显然地,我们可以每次增广最短路,把最短路压榨干净后,这条路就在残量网络中OUT了。而这条跑满流的边也就是关键边

每一个增广路意味着至少有一条关键边出现,而增广过程的迭代次数也就可以看做是每条边成为关键边的次数。

这样总迭代次数为 \(O(V\cdot E)\) ,加上0/1最短路的 \(O(E)\),总复杂度也就是 \(O(V \cdot E^{2})\)

局限性

因为 \(O(E)\)\(O(V^{2})\)同阶,那复杂度也可以看做是 \(O(V^{5})\)

这时候就有人说了:虽然复杂度已经降到了一个多项式的程度,但还是难以让人接受,有没有更快更好的算法呢?

有的,有的,包有的兄弟。

咱先想想EK为啥不够快:
我们可以很显然地艰难地发现,EK每次只会跑一条最短路,但我们最短路可能不止一条啊。

基于这一点,我们继续进行优化。

Dinic算法

没有什么是一个BFS或一个DFS解决不了的;如果有,那就两个一起。

Dinic的核心思想就是在EK的基础上进行多路增广。

这需要搞出分层图,而这里分层图的定义可以理解为:对于每个点,到达源点s的最短路大小。也可以认为是一个点的深度。

分完层之后有什么好处呢?首先层内边和反向边都会被无视掉。
其次我们可以发现,此时所有的最短路都在图上呈现。这样DFS一遍就能把这张图上的最短路全都干掉。

似乎这样可以跑得飞快。

但是的但是,我们会重复搜到同一个点,但通过这个点的流量不一定已经到达最大值,所以不能直接标记掉,但无标记的DFS直接爆炸。

so 优化了个寂寞???

No!No!No!

我们再来加两只优化(咋这么多优化)。

代码解释。

struct node{int to,c,lp;};
vector<node>g[N];
int dep[N],pos[N];
int n,m,S,T;
inline void add(int from,int to,int c)//存边包括正向边和反向边
{g[from].push_back((node){to,c,(int)g[to].size()});//正向g[to].push_back((node){from,0,(int)g[from].size()-1});//反向,g[from]已经加入新值,下标size-1
}
inline void fc(int s)//深度信息
{for(int i=0;i<=n;i++)dep[i]=-1;queue<int>q;//储存节点编号dep[s]=0;q.push(s);while(!q.empty()){int u=q.front();q.pop();for(auto v:g[u]){if(v.c>0&&dep[v.to]<0)//容量大于0且没被计算过{dep[v.to]=dep[u]+1;q.push(v.to);}}}
}
inline int dfs(int u,int t,int f)
{if(u==t||!f) return f;int ans=0;for(int &i=pos[u];i<g[u].size();i++)//当前弧优化,重复经过同一个节点时,跳过已经跑满的边,巧妙地更新pos值实现{auto &x=g[u][i];//g[u][i]的信息要被修改if(x.c>0&&dep[u]+1==dep[x.to])//有容量且层数相邻{int frz=dfs(x.to,t,min(f,x.c));if(frz>0){x.c-=frz;g[x.to][x.lp].c+=frz;f-=frz;ans+=frz;if(!f) break;//剪枝,现在找不到妹子,以后也找不到。}}}return ans;
}
inline ll dinic(int s,int t)
{ll max_flow=0;while(1){fc(s);if(dep[t]==-1)return max_flow;for(int i=0;i<=n;i++)pos[i]=0;max_flow+=dfs(s,t,inf); }
}
inline void solve()
{n=read();m=read();S=read();T=read();for(int i=1;i<=m;i++){int x,y,c;x=read();y=read();c=read();add(x,y,c);}write(dinic(S,T));
}

完整板子:

#ifdef ONLINE_JUDGE 
#else 
#define Qiu_Cheng 
#endif 
#include <bits/stdc++.h> 
// #define int long long 
using namespace std; 
typedef long long ll; 
const int N=205,mod=1e9+7,inf=INT_MAX; 
// const int mod1=469762049,mod2=998244353,mod3=1004535809;
// const int G=3,Gi=332748118; 
// const int M=mod1*mod2;
inline int read()
{int x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){f=-1;}c=getchar();}while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c-'0');c=getchar();}return x*f;
}
inline void write(ll x)
{if(x<0){putchar('-');x=-x;}if(x>9) write(x/10);putchar(x%10+'0');
}
struct node{int to,c,lp;};
vector<node>g[N];
int dep[N],pos[N];
int n,m,S,T;
inline void add(int from,int to,int c)
{g[from].push_back((node){to,c,(int)g[to].size()});g[to].push_back((node){from,0,(int)g[from].size()-1});
}
inline void fc(int s)//深度信息
{for(int i=0;i<=n;i++)dep[i]=-1;queue<int>q;//储存节点编号dep[s]=0;q.push(s);while(!q.empty()){int u=q.front();q.pop();for(auto v:g[u]){if(v.c>0&&dep[v.to]<0){dep[v.to]=dep[u]+1;q.push(v.to);}}}
}
inline int dfs(int u,int t,int f)
{if(u==t||!f) return f;int ans=0;for(int &i=pos[u];i<g[u].size();i++)//当前弧优化{auto &x=g[u][i];//g[u][i]的信息要被修改if(x.c>0&&dep[u]+1==dep[x.to])//有容量且层数相邻{int frz=dfs(x.to,t,min(f,x.c));if(frz>0){x.c-=frz;g[x.to][x.lp].c+=frz;f-=frz;ans+=frz;if(!f) break;//剪枝}}}return ans;
}
inline ll dinic(int s,int t)
{ll max_flow=0;while(1){fc(s);if(dep[t]==-1)return max_flow;for(int i=0;i<=n;i++)pos[i]=0;max_flow+=dfs(s,t,inf); }
}
inline void solve()
{n=read();m=read();S=read();T=read();for(int i=1;i<=m;i++){int x,y,c;x=read();y=read();c=read();add(x,y,c);}write(dinic(S,T));
}
signed main() 
{ 
#ifdef Qiu_Cheng freopen("1.in","r",stdin); freopen("1.out","w",stdout); 
#endif // ios::sync_with_stdio(false); //cin.tie(0); cout.tie(0); // int QwQ; // cin>>QwQ; // while(QwQ--)solve(); solve(); return 0; 
}
//12 825076913 0 173167432
//  6666   66666  666666 
// 6    6  6   6      6 
// 6    6  6666      6 
// 6    6  6  6    6 
//  6666   6   6  6666666//g++ -O2 -std=c++14 -Wall "-Wl,--stack= 536870912 " cao.cpp -o cao.exe

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

金砖技能大赛-内存取证

检材链接 :https://pan.baidu.com/s/1tYgIicCqJExmaMLYa3YeSA 提取码:lulu按照惯例先打印出信息 1.从内存中获取到用户admin的密码并且破解密码,以Flag{admin,password} 形式提交(密码为 6 位); 先使用lsadump指令看看python2 vol.py -f /文件路径/文件 hashdump最后一个是…

script 标签放在 HTML 文档的 body 内底部

以下是将 <script> 标签放在 HTML 文档的 <body> 内底部的几个重要原因: 1. 页面加载顺序和性能优化原理:当浏览器解析 HTML 文档时,它会按顺序执行遇到的元素。如果 <script> 标签位于 <head> 中或 <body> 的顶部,浏览器会在下载和执行脚本…

折腾笔记[4]-cuda的hello-world

在window11上搭建cuda开发环境并编译hello world程序;摘要 在window11上搭建cuda开发环境并编译hello world程序; 关键信息编译器:cuda nvcc 12.4.131 平台:windows11原理简介 cuda简介 CUDA(Compute Unified Device Architecture,统一计算架构)是由英伟达所推出的一种集成技术…

13. 滑块控件

一、抽象滑块QScrollBar、QSlider 和 QDail 都是从 QAbstractSlider 类继承而来的,因此它们的多数方法是相同的。我们可以在终端中使用 pip 安装 pyside6 模块。 pip install pyside6QAbstractSlider 类的常用方法如下: # 实例方法 orientation() -> Qt.Orientation …

3.数据类型

3.1字符串1.正常字符串使用 或 "" 包裹起来2.注意转义字符 \ \ 显示’ \n 换行 \t tab \u4e2d \u### Unicode字符3.多行字符串编写 4.模板字符串5.字符串长度 str(变量名).length6.字符串的可变性,不可变String Buffer和StringBuilder都是可变的,String…

h5如何解决移动端适配问题

使用响应式布局(Responsive Layout)原理:通过CSS媒体查询(Media Queries)根据设备的屏幕尺寸、分辨率等属性来动态调整页面的布局和样式。媒体查询允许您针对不同的媒体类型(如屏幕、打印等)和条件(如屏幕宽度、高度、设备方向等)应用不同的CSS规则。 示例代码:例如,…

第15章 流与IO

第15章 流与IO 15.1 .NET 流的架构 .NET 流的架构主要包含三个概念:** 后台存储 、 装饰器 以及 流适配器 **,如图所示: C7.0 核心技术指南 第7版.pdf - p655 - C7.0 核心技术指南 第 7 版-P655-20240216192328 ​​ 其中** 后台存储 和 装饰器 **为流。后台存储流:负责处理…

超全性能调优标准制定指南,你一定不能错过!

0 前言 我有个朋友说他们国企的系统从未性能调优,功能测试完就上线,线上也没性能问题,何必还做性能调优? 本文搞清:为什么要做性能调优? 啥时开始做? 做性能调优是不是有标准?1 为啥做性能调优?有些性能问题是慢慢产生,到了时间就自爆 更多性能问题是由访问量波动导致…

第16章 网络

第16章 网络 纲要 .NET Framework 在 System.Net.*命名空间中包含了支持各种网络标准的类,支持的标准包括 HTTP、TCP/IP 以及 FTP 等。以下列出了其中的主要组件:​Webclient​ 类 支持通过 HTTP 或者 FTP 执行简单的下载/上传操作。​WebRequest​ 和 WebResponse​ 类 可以…

第13章 诊断

第13章 诊断 13.1 条件编译 预编译的指令见 4.16 预处理指令,我们这里的条件编译用到的指令有:​#if​​、#else​​、#endif​​、#elif​​ 条件编译指令可以进行 与 ​&&​ ​、 或 ​||​ ​、 非 ​!​ 运算。预定义指令可以通过三种方式定义:在文件中通…

第14章 并发与异步

第14章 并发与异步 14.2 线程 进 程提供了程序执行的独立环境, 进 程持有 线 程,且至少持有一个 线 程。这些 线 程共享 进 程提供的执行环境。 14.2.1 创建线程 创建线程的步骤为:实例化 ​Thread​ ​ 对象,通过构造函数传入 ​ThreadStart​ ​ 委托。 调用 ​Thread…

Sqlserver With as 实现循环递归

一、脚本示例declare @Separator varchar(10), @str varchar(100) declare @l int, @i int select @Separator=,,@str=111,22,777,99,666 select @i = len(@Separator), @l = len(@str); with cte7 as ( select 0 a, 1 b union all select b, charindex(@Separator, @str, b)+@…