网络流学习笔记
网络流
网络指有向图
每条边都有权值都有一个给定的值称为,若则,特别的有:和,称为源点和汇点。
是一个有向图,图中每条边 有一个非负的容量值
而且,如果边集合 包含一条边 , 则图中不存在反方向的边 。
设是定义在二元组的实数函数
- 容量限制:对于每条边,流经该边的流量不得超过该边的容量.,若则称为满流
- 斜对称性:每条边的流量与其相反边的流量之和为 0,即
- 流守恒性:从源点流出的流量等于汇点流入的流量,即:
称为的流函数,,称为边的流量称为边的剩余容量
整个网络的流量及从源点出发所有流量之和
网络流的相关概念
- 剩余流量表示这条边容量与流量之差
假定有一个流网络 , 其源结点为 , 汇点为 。设 为图 中的一·
个流,考虑结点对 定义残存容量 如下:
- 对于一个流,残量网络:网络中的节点和剩余流量大于的边构成的子图
残量网络中包括了那些还剩了流量空间的边构成的图,也包括虚边。
类似一个流量为的流网络,但该网络并不满足我们对流网络的定义,因
为它可能包含边 和它的反向边 。
3.设为原图的一个流 将边 的流量增加 , 但减少 。
在残存网络中将流量推送回去也称为抵消操作 (cancellation) 。
4. 增广路径:在残量网络 中,一条从源点到汇点的路径被称为增广路。
5. 对于将点集划分为 两个集合,使,定义切割的净流量为
切割容量为
最大流
我们有一张图,要求从源点流向汇点的最大流量(可以有很多条路到达汇点),就是我们的最大流问题。
Edmonds- Karp
定义。最大的流量问题是将尽可能多的流量从源头路由到汇,换句话说,找到流量
若一条的路径上的每一条边都大于零,我们称它为增广路径,便是通过求增广路径来实现的。其复杂度为
增广的时候要注意建造反向边,原因是这条路不一定是最优的,这样子程序可以进行反悔。假如我们对这条路进行增广了,那么其中的每一条边的反向边的流量就是它的流量。
当一条边的流量时,根据斜对称性质,它的反向边流量,此时必定有,所以EK算法除了遍历原图的正向边以外还要考虑遍历每条反向边
算法步骤
- 我们就源点BFS ,碰到汇点就停,然后增广(每一条路都要增广)。
- 按照我们找的增广路在重新走一遍。走的时候把这条路的能够成的最大流量减一减,然后给答案加上最小流量就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N = 205,M = 100005,inf = 1 << 29;
typedef long long ll;
int h[N],ne[M],w[M],e[M],pre[N],incf[N],idx,t,s,n,m,maxflow;
bool st[N];
void add(int u,int v,int val)
{
e[++idx] = v,w[idx] = val,ne[idx] = h[u],h[u] = idx;
}
int bfs()
{
for(int i = 1 ; i <= n ; i ++ )st[i] = 0;
queue<int>q;
q.push(s),st[s] = 1;
incf[s] = inf;//根据定义
while(q.size())
{
int u = q.front();q.pop();
for(int i = h[u];i;i=ne[i])
if(w[i]){//标记
int v = e[i];
if(st[v])continue;
incf[v] = min(incf[u],w[i]);//记录剩余容量最小值
pre[v] = i;//记录方案
q.push(v),st[v] = 1;
if(v == t)return 1;//找到汇点
}
}
return 0;
}
void update()
{
ll x = t;
while(x!=s)
{
int i = pre[x];
w[i] -=incf[t];
w[i^1] += incf[t];
x = e[i^1];
}
maxflow += incf[t];
}
int main()
{
cin >> n >> m >> s >> t;
idx = 1,maxflow = 0;
for(int u,v,val,i = 1 ; i <= m ; i ++ )
{
scanf("%d%d%d",&u,&v,&val);
add(u,v,val);
add(v,u,0);
}
while(bfs())update();
cout << maxflow << endl;
}
Dinic
由于每次算法都要遍历整个残量网络,找到一条增广路径,还有进一步优化的空间。设表示从到最少需要经过的边数在残量网络中满足的边构成的子图被称为分层图
- 在残量网络上BFS求节点的层次,构造分层图
- 在分层图上BFS寻找增广路在回溯时更新剩余流量。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5,M = 5e5 + 5,inf = 1<<29;
int n,m,idx = 1,ne[M],h[N],e[M],w[M],d[N],s,t;
long long maxflow;
queue<int>q;
void add(int x,int y,int z)
{
e[++idx] = y,ne[idx] = h[x],w[idx] = z,h[x] = idx;
}
bool bfs()
{
memset(d,0,sizeof (d));
while(q.size())q.pop();
d[s] = 1,q.push(s);
while(q.size())
{
int x = q.front();q.pop();
for(int i = h[x] ;i ; i = ne[i] )
if(w[i] && !d[e[i]])
{
q.push(e[i]);
d[e[i]] = d[x] + 1;
if(e[i] == t)return 1;
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x == t)return flow;
int res = flow,k;
for(int i = h[x]; i && res ; i = ne[i])
if(w[i] && d[e[i]] == d[x] + 1)
{
k = dinic(e[i],min(res , w[i]));
if(!k) d[e[i]] = 0;
w[i] -= k;
w[i^1] += k;
res -= k;
}
return flow - res;
}
int main()
{
cin >> n >> m >> s >> t;
idx = 1;
for(int x,y,z,i = 1 ; i <= m ; i ++ )
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,0);
}
int flow = 0;
while(bfs())
while (flow = dinic(s,inf))maxflow += flow;
cout << maxflow << endl;
}
“风雪越是呼啸,雪莲越是绽放”
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App