下面给网络里增加一个因素:费用。假设每条边除了有一个容量限制以外,还有一个单位流量所需的费用。在最小费用流问题中,平行边变得有意义:可能会有两条从u到v的弧,费用分别为1和2。在没有费用的情况下,我们可以把二者合并,但由于费用的出现,我们无法合并这两条弧。再如,若边(u,v)和(v,u)均存在,且费用都是负数,则“同时从u流向v和从v流向u”是个很不错的主意。为了方便叙述算法,我们先假定图中不存在平行边和反向边。这样就可以用邻接矩阵cap和cost保存各边的容量和费用。为了便于增广,对于一条边(u,v),我们规定cap[u][v]=0并且cost[v][u]=-cost[u][v],表示沿着(u,v)的相反方向增广时,费用减少cost[u][v]。下面直接给出算法,和EK算法类似,但每次用Bellman-Ford算法,而非BFS找增广路。只要初始流是该流量下最小费用可行流,每次增广后的新流都是新流量下的最小费用流。另外费用值是可正可负的。
queue<int> q;
int d[maxn];
memset(flow, 0, sizeof(flow));
c = f = 0;
while(true){
//bellman-ford算法开始,在残留网络中找s-t最短路
bool inq[manx];
for(int i = 0; i < n; i++) d[i] = (i==s ? 0 : INF);
memset(inq,0,sizeof(inq));
q.push(s);
while(!q.empty)){
int u = q.front(),q.pop();
for(int i = 0; i < n; i++){
if(cap[u][i]>flow[u][i] && d[i]>d[u]+cost[u][i]){
d[i] = d[u]+cost[u][i];
p[i] = u;
if(!inq[i]){
q.push(i);
inq[i] = 1;
}
}
}
} //bellman-ford算法结束
if(d[t]==INF) break; //说明汇点不可达,辨明当前流已经是最小费用最大流
int a = INF;
for(int i = t; i!=s; i = p[i]) a = (a>cost[p[i]][i]-flow[p[i]][i] ? cost[p[i]][i]-flow[p[i]][i] : a);
for(int i = t; i!=s; i = p[i]){
flow[p[i]][i] += a;
flow[i][p[i]] -= a;
}
c += d[t]*a;
f += a;
}
运行结束后,f和c分别保存最大流流量和该流量下最小费用。我们要是把邻接矩阵表示方式改成邻接表的表示方式,在此情况下,我们可以去掉“不存在平行边和反向边”的限制(因为邻接表中可以有起点和重点相同的边,而算法不需要经过任何修改, 邻接表的详情见此,这篇文章后半部分,讲述了邻接表的实现思想)