1.最大流
由源点
s
s
s到汇点
t
t
t的最大流量。
图的最大流
≤
\leq
≤将图进行割边的流量(任意割)
证明最大流等于最小割:
简略版)
对于一张图网络,假设开始是一个连通图且源点
s
s
s和汇点
t
t
t都在一个集合里,设图网络的最大流为
F
F
F,那么
F
F
F等于从源点
s
s
s流出的流量(假设源点有无限的流量)且
F
F
F等于从汇点
t
t
t流入的流量,当对连通图进行割边后集合会被分成两个集合,源点与汇点分别在两个集合中,此时源点流出的流量等于汇点流入的流量等于割边的流量。即图网络的最大流等于最小割。
思想
发现每次从 s s s到 t t t进行对残量网络的一条路径减去路径的最小值,当沿残量网络无法到达 t t t时,即构造出了割边。复杂度为 O ( n 2 m ) O(n^2m) O(n2m),但实际远远达到不了这里。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long ;
const int N = 5e3+10 ;
ll e[N<<1],ne[N<<1],w[N<<1],h[N<<1],k;
void add(int a,int b,int c){
e[++k]=b,ne[k]=h[a],w[k]=c,h[a]=k;
}
ll dep[N];
bool bfs(int s,int t){
memset(dep,0,sizeof dep);
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int t=q.front();q.pop();
for(int i=h[t];i;i=ne[i]){
int j=e[i];
if(w[i] and !dep[j]){
dep[j]=dep[t]+1;
q.push(j);
}
}
}
return dep[t];
}
ll dfs(int s,int t,ll in){
if(s==t)return in;
ll out=0;
for(int i=h[s];i;i=ne[i]){
int j=e[i];
if(w[i] and dep[j]==dep[s]+1){
ll x=dfs(j,t,min(in,w[i]));
w[i]-=x;
w[i^1]+=x;
out+=x;
in-=x;
}
}
if(out==0)dep[s]=0;
return out;
}
ll dinic(int s,int t){
ll ans=0;
while(bfs(s,t))ans+=dfs(s,t,1e18);
return ans;
}
template<class Tp=int>
Tp read(){
char ch=getchar();bool f=1;Tp x=0;
while(!isdigit(ch)){
if(ch=='-')f=false;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?x:-x;
}
template<class Tp=int>
void print(Tp n){
if(n>9)print(n/10);
putchar(n%10|'0');
}
int main()
{
k=1;
ios::sync_with_stdio(false);
int n,m,s,t;
n=read(),m=read(),s=read(),t=read();
for(int i=0,a,b,c;i<m;i++){
a=read(),b=read(),c=read();
add(a,b,c);
add(b,a,0);
}
print<ll>(dinic(s,t));
}
2.最小费用最大流
很明显是在最小费用的基础上求最大流。
思想
每次使用dijskra或spfa以单位费用最小找到从 s s s到 t t t的最小费用路径,和构造最大流一样构造割边即可,复杂度为 O ( m n l o g 2 n ) O(mnlog_2n) O(mnlog2n) O ( n 2 m ) O(n^2m) O(n2m)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long ;
const int N = 5e4+10 ;
const ll inf = 1e10 ;
ll e[N<<1],ne[N<<1],h[N<<1],w[N<<1],c[N<<1];
int k=1;
void add(int a,int b,int c,int d){
e[++k]=b,ne[k]=h[a],h[a]=k;
w[k]=c,::c[k]=d;
}
ll dis[N];
bool vis[N];
int dep[N];
ll flow[N];
int pre[N],last[N];
bool spfa(int s,int t){
memset(dis,0x7f,sizeof dis);
memset(flow,0x7f,sizeof flow);
memset(pre,-1,sizeof pre);
memset(vis,0,sizeof vis);
queue<int>q;
q.push(s);
pre[s]=0;
vis[s]=true;
dis[s]=0;
while(!q.empty()){
int t=q.front();q.pop();
vis[t]=false;
for(int i=h[t];i;i=ne[i]){
int j=e[i];
if(w[i]&&dis[j]>dis[t]+c[i]){
last[j]=i;
dis[j]=dis[t]+c[i];
pre[j]=t;
flow[j]=min(flow[t],w[i]);
if(!vis[j]){
q.push(j);
vis[j]=true;
}
}
}
}
return pre[t]!=-1;
}
void dinic(int s,int t){
ll F=0,D=0;
while(spfa(s,t)){
int x=t;
F+=flow[t];
D+=flow[t]*dis[t];
while(x!=s){
w[last[x]]-=flow[t];
w[last[x]^1]+=flow[t];
x=pre[x];
}
}
cout<<F<<" "<<D;
}
int main()
{
ios::sync_with_stdio(false);
int n,m,s,t;
cin>>n>>m>>s>>t;
for(int i=0,u,v,w,c;i<m;i++){
cin>>u>>v>>w>>c;
add(u,v,w,c);
add(v,u,0,-c);
}
dinic(s,t);
return 0;
}