费用流模板——SPFA

17 篇文章 0 订阅
6 篇文章 0 订阅

先解释一下费用流:

对于一个网络, 我们可能会有多组最大流, 现在我们给每条边附上一个费用系数, 每条边产生的费用 = 这条边的流量 × 这条边的费用系数,常见的问题是求最小费用最大流。

做法非常简单,每次找一条费用最小的增广路,并确定这条增广路上最小的流量,整条增广路上的弧减去这个流量, 反向弧加上, 直到没有增广路为止。

答案是每次的最小流量乘上费用系数的和。

这么做最大流是一定可以保证的,那为什么费用会最小呢?

我也不会,某人说:“你听过最小生成树克鲁斯卡尔算法吗?”。

先规定一下要用的东西:

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;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值