题解:P3381 【模板】最小费用最大流
更好的阅读体验
这是一道模板题。在做这道题之前,你应该要学会:
- 【模板】网络最大流
- 【模板】负环
对于网络
(V, E) ,边(u, v) 带容量f(u, v) 和费用c(u,v) 。求出S \to T 的所有最大流中所选边费用最小的方案,称作网络的最小费用最大流(MCMF)。
对此,我们以求解最大流的 Edmonds-Karp 算法为基础。EK 的过程是,每次选择一条
在求解最小费用的过程中,我们以
我们想想这个东西为什么是对的。
EK 结束的条件是图中不存在合法路径。一方面,根据最大流最小割定理,一个流是最大流当且仅当途中不存在增广路径。所以求出最大流的正确性显然。
另一方面,我们需要说明这种方案所需要的费用是最少的。记处于一个局面时的最小费用为
综上所述,我们证明了该算法的正确性。
我们回到实现上。我们可以使用 SPFA 算法寻找最短简单路径,在松弛的时候记录每个点的前驱。随后我们沿着记录的路径进行增广,同时将最小费用加上路径的边权和。
由于一次增广至少使最大流增加
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define N 5006
#define M 50006
using namespace std;
int n,m;
struct MCMF_Graph{//by dyc2022
int head[N],tot=2,s,t;
int dis[N],incf[N],pre[N],vis[N],maxflow,mincost;
struct Node{int nxt,to,w,c;}E[M<<1];
void addedge(int u,int v,int w,int c){E[tot]={head[u],v,w,c},head[u]=tot++;}
void addflow(int u,int v,int w,int c){addedge(u,v,w,c),addedge(v,u,0,-c);}
int SPFA()
{
for(int i=1;i<=n;i++)dis[i]=1e15;
queue<int> q;
q.push(s),dis[s]=0,incf[s]=1e15,incf[t]=0;
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=0;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].to,w=E[i].w,c=E[i].c;
if(!w||dis[v]<=dis[u]+c)continue;
dis[v]=dis[u]+c,incf[v]=min(incf[u],w),pre[v]=i;
if(!vis[v])q.push(v),vis[v]=1;
}
}
return incf[t];
}
void EK()
{
maxflow+=incf[t];
for(int u=t;u!=s;u=E[pre[u]^1].to)
{
E[pre[u]].w-=incf[t],E[pre[u]^1].w+=incf[t];
mincost+=incf[t]*E[pre[u]].c;
}
}
void getflow(){while(SPFA())EK();}}G;
main()
{
scanf("%lld%lld%lld%lld",&n,&m,&G.s,&G.t);
for(int i=1,u,v,w,c;i<=m;i++)
scanf("%lld%lld%lld%lld",&u,&v,&w,&c),G.addflow(u,v,w,c);
G.getflow();
printf("%lld %lld\n",G.maxflow,G.mincost);
return 0;
}