先解释一下费用流:
对于一个网络, 我们可能会有多组最大流, 现在我们给每条边附上一个费用系数, 每条边产生的费用 = 这条边的流量 × 这条边的费用系数,常见的问题是求最小费用最大流。
做法非常简单,每次找一条费用最小的增广路,并确定这条增广路上最小的流量,整条增广路上的弧减去这个流量, 反向弧加上, 直到没有增广路为止。
答案是每次的最小流量乘上费用系数的和。
这么做最大流是一定可以保证的,那为什么费用会最小呢?
我也不会,某人说:“你听过最小生成树克鲁斯卡尔算法吗?”。
先规定一下要用的东西:
int dis[Maxn], d[Maxm * 10], bz[Maxn], ans; //dis[x]表示从S到x的距离,d是队列,bz表示在队列里。
int tot = 1, final[Maxn], last[Maxn]; //final是前向星的,last表示这个点是由哪一条弧过来的。
struct edge {
int to, next, r, w, fr; //to,next是前向星的,r是弧的容量,w是费用系数,方便后面的操作,fr表示这条弧的起点。
}a[Maxm];
void link(int x,int y,int r,int w) {
a[++tot].next = final[x], a[tot].to = y, a[tot].fr = x, a[tot].r = r, a[tot].w = w, final[x] = tot;
a[++tot].next = final[y], a[tot].to = x, a[tot].fr = y, a[tot].r = 0, a[tot].w = -w, final[y] = tot;
} //前向星加边
bool SPFA() {
memset(dis, 127 ,sizeof(dis));
dis[S] = 0;
d[1] = S; bz[S] = 1;
for(int st = 1, en = 1; st <= en; st ++) {
int x = d[st];
bz[x] = 0;
for(int k = final[x]; k; k = a[k].next){
int y = a[k].to;
if(a[k].r > 0 && dis[x] + a[k].w < dis[y]){
last[y] = k;
dis[y] = dis[x] + a[k].w;
if(!bz[y]) {
d[++ en] = y;
bz[y] = 1;
}
}
}
}
return dis[T] != dis[0]; //返回是否有增广路。
}
void solve() {
while (SPFA()) {
int minr = INF;
for(int i = last[T]; i; i = last[a[i].fr]) minr = min(minr, a[i].r);
for(int i = last[T]; i; i = last[a[i].fr]) {
a[i].r -= minr; a[i ^ 1].r += minr;
ans1 += minr; ans2 += a[i].w * minr;
}
}
}