在学最小费用最大流之前,首先要先了解网络流的基本知识
好,如果你已经会做最大流并且想学一学怎么计算最小费用最大流那就看完这篇文章吧
最小费用最大流,即一幅图,除了网络流的基本信息外,每条边还会有每流过1流量消耗的费用的信息,要你求的是保证最大流的前提下的最小的费用
对于这样的问题呢,有很多做法,这里我就介绍一个较为经典的spfa算法吧
首先,在做最小费用最大流之前,先考虑最大流问题的做法,你找到一条可行流,然后增广就好了,直到不能再增广。最小费用最大流差不多也是这个思路,只是每次都找花费最小的路增广,这么做显然是最小费用的,而且是能做到最大流。
那么怎么找到花费最小的增广路呢?考虑spfa,做一遍所有剩余流量为正的边的最短路,每一条边的距离为这条边的费用,做spfa的时候只要记录一下当前这个最短路是从哪里转移过来的就好了,每找到一个增广路就统计一下这条路上的最短路,然后处理一下加到答案,重新spfa,直到没有可行增广路
洛谷模板题https://www.luogu.org/problemnew/show/P3381
代码:
#include<cstdio>
#include<cstring>
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
const int maxn=5002,maxm=100003,INF=0x7f7f7f7f;
int N,M,S,T,maxflow,mincost;
int head[maxn],nxt[maxm],tow[maxm],vau[maxm],cost[maxm],tmp=1;
inline void addb(const int u,const int v,const int w,const int f)
{
tmp++;
nxt[tmp]=head[u];
head[u]=tmp;
tow[tmp]=v;
vau[tmp]=w;
cost[tmp]=f;
}
int dis[maxn],fr[maxn];
int Q[maxn*10],l,r;
bool Hash[maxn];
inline int spfa()
{
memset(dis,0x7f,sizeof(dis));
dis[S]=0;
Hash[S]=1;
l=r=0;
Q[++r]=S;
while(l<r)
{
const int u=Q[++l];
for(register int i=head[u];~i;i=nxt[i])
{
const int v=tow[i];
if(vau[i]>0)
{
if(dis[u]+cost[i]<dis[v])
{
dis[v]=dis[u]+cost[i];
fr[v]=i;
if(!Hash[v])
{
Hash[v]=1;
Q[++r]=v;
}
}
}
}
Hash[u]=0;
}
return dis[T];
}
void calc()
{
int flow=INF,cos=0;
int u=T;
while(u!=S)
{
flow=min(flow,vau[fr[u]]);
cos+=cost[fr[u]];
u=tow[fr[u]^1];
}
maxflow+=flow;
mincost+=cos*flow;
u=T;
while(u!=S)
{
vau[fr[u]]-=flow;
vau[fr[u]^1]+=flow;
u=tow[fr[u]^1];
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&N,&M,&S,&T);
for(register int i=1;i<=M;i++)
{
int u,v,w,f;
scanf("%d%d%d%d",&u,&v,&w,&f);
addb(u,v,w,f);
addb(v,u,0,-f);
}
while(spfa()!=INF)calc();
printf("%d %d",maxflow,mincost);
return 0;
}
为什么要用spfa呢?那是因为写起来简单,但是spfa有个注意点,那就是要费用不能有负数,否则就会陷入死循环,还有就是这种算法对于有一些图跑的不是很快,所以对于最小费用最大流还有zkw费用流算法(当然zkw也不是十全十美的,不同的算法有不同的优点),具体在这里就不赘述啦