[笔记]网络流--最大流问题
[笔记]网络流--最大流问题
原题链
算法用途
解决网络流最大流问题,一般使用的是EK算法或是Dinic算法,由于Dinic的复杂度更优秀,所以我用的是Dinic,这里也只讲这个算法EK不会啊
问题描述
一.几个概念
1.网络:一个网络 \(G = (V,E)\)是一张有向图,图中每条有向边\((x,y) ∈ E\)都有一个给定的权值\(c(x,y)\),称为变的容量.图中还有两个特殊的给定的点\(S,T\)称为源点和汇点,也就是起点和终点.\(f(x,y)\)函数是指一个满足条件的流量方案,满足:
\(f\)函数也可以成为边的流量.
2.最大流问题:对于一个给定的网络,合法的流函数\(f\)有很多,使得整个网络流量\(Σ_{(s,v)∈E}f(s,v)\)最大,(S是源点)的流函数称为网络的最大流.
算法描述
一.大致流程
1.用BFS找出可行的增广路(也就是可以增加流量的从源点到汇点的路径),并预处理出各个节点的深度.
2.再用dfs对增广路增加流量,知道不存在增广路.
3.重复一上两个步骤知道不能增广为止.
二.具体实现
首先对每条弧存一条反向弧,初始流量为0,当正向弧剩余流量减少时,反向弧剩余流量随之增加,这样就为每条弧提供了一个反悔的机会,可以让一个流沿反向弧退回而去寻找更优的路线.对于一个网络流图,用bfs将图分层,只保留每个点到下一个层次的弧,目的是减少寻找增广路的代价.对于每一次可行的增广操作,用dfs的方法寻找一条由源点到汇点的路径并获得这条路径的流量c.根据这条路径修改整个图,将所经之处正向边流量减少c,反向边流量增加c.如此反复直到bfs找不到可行的增广路线.
三.优化
当前弧优化
对于一个节点\(x\),当它在\(dfs\)中走到了第\(i\)条弧时,前\(i-1\)条弧到汇点的流一定已经被流满而没有可行的路线了.那么当下一次再访问\(x\)节点的时候,前\(i-1\)条弧就可以被删掉而没有任何意义了.所以我们可以在每次枚举节点\(x\)所连的弧时,改变枚举的起点,这样就可以删除起点以前的所有弧以达到优化的效果.
整体流量优化
对于每一个节点,记录可以向下流的总流量再返回,就可以减少递归的次数.
代码
#include <bits/stdc++.h>
using namespace std;
struct node{
long long to = 0,next = 0,w = 0;
}edge[50010];
long long fir[50010],cur[50010];
long long n,m,s,t,tot = 1;
long long dep[50010];
long long maxx = 0;
const int inf = INT_MAX;
void add(long long x,long long y,long long z){
tot++;
edge[tot].to = y;
edge[tot].w = z;
edge[tot].next = fir[x];
fir[x] = tot;
tot++; //建反向边方便反悔
edge[tot].to = x;
edge[tot].w = 0;
edge[tot].next = fir[y];
fir[y] = tot;
return;
}
bool bfs(){
queue < int > q;
while(!q.empty())q.pop();
memset(dep,0,sizeof(dep));
q.push(s);dep[s] = 1;
while(!q.empty()){
int x = q.front();
q.pop();
for(int i = fir[x];i;i = edge[i].next){
if(edge[i].w > 0 && dep[edge[i].to] == 0){
q.push(edge[i].to);
dep[edge[i].to] = dep[x] + 1;
if(edge[i].to == t)return true; //找到增广路
}
}
}
return false;
}
long long dinic(int x,long long flow){
if(x == t)return flow;
long long now = 0;
for(int i = cur[x];i && flow;i = edge[i].next){
cur[x] = i; //记录当前搜的点,当前弧优化
if(edge[i].w != 0 && dep[edge[i].to] == dep[x] + 1){
int k = dinic(edge[i].to,min(edge[i].w,flow));
if(k == 0)dep[edge[i].to] = 0;
now += k;
edge[i].w -= k;
edge[i ^ 1].w += k;
flow -= k;
}
}
return now;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
for(int i = 1;i <= m;i++){
long long x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z);
}
while(bfs()){
memmove(cur,fir,sizeof(cur));
maxx += dinic(s,inf);
}
printf("%lld\n",maxx);
return 0;
}