最大流(SAP)

sap

思路

本蒟蒻太蒟,只会这个算法
首先,我们来几个前置知识。

  • 增广路径就是指从起点到终点的路径。
  • 我们把一个节点离终点的最短距离称为距离标号。

因为增广路径意味着可以将答案加大,所以说我们每次就找增广路径就行了。
但是,你所找的增广路径不一定是最优的,所以说我们就要增加反悔机制。

反悔机制就是我们每次在将水流过去后,我们在加一条管道,这条管道就是让我们流过去的水可以流回来,这就是一个反悔机制,也就是下面的代码。

E[xy].val -= delta, E[xy ^ 1].val += delta;//delta就是你流过去多少水

注意,如果用链式前向星的话,第一条边的编号要从 2 2 2 开始,这样才能完成反悔机制。

但是我们要是每次都随便找增广路径的话,那可能会被卡掉。
所以说我们就要想个办法,于是我们就可以每次都找最短的增广路径,就可以用到距离标号了。

每次从起点出发,看一下跟自己相连的节点的距离编号如果是自己的距离编号减一,就往那里走。

if(d[now]==d[to]+1)
{
    枚举to
}

但是我们在找到一个增广路径的时候,d值可能会改变,怎么办呢?
我们就来看看从now能不能走到某一个节点,要是不能,我们就将他的距离编号变成与他相连的最小的距离编号值加一。(也就可以不用初始化距离编号,毕竟你就把刚开始的距离编号当成不合法的做就可以了)
还有一个优化,叫做GAP优化,就是如果d值有断层,那么就可以直接说明你现在找不出增广路径了。
我们还可以在增广一个节点的时候把他的分岔一次性全部增广了,不必分为几条增广路径增广。

代码(有注释)

/*
flow 就是你的答案,找到增广路径可以将答案增大,每次找增广路径增大答案就可以了。
augco 从i为起点的最大增广容量
augc 记录剩下的需要增广的量
mind 与你相连的最小的d值
*/
int aug(int x, int augco) {
    if (x == T)//如果到了终点,就直接返回你增广的量
        return augco;
    int augc = augco, mind = cnt - 1, delta;
    for (int xy = last[x]; xy; xy = E[xy].next)
        if (E[xy].val > 0) {
            if (d[x] == d[E[xy].to] + 1) {//如果i能到j
                delta = aug(E[xy].to, min(E[xy].val, augc));//min中的意思就是你原本能增广的量和这条管子能增广的量的最小值
                augc -= delta, E[xy].val -= delta, E[xy ^ 1].val += delta;
                if (d[1] >= cnt)//如果距离标号的大小大于所有的点数,就说明没有增广路径了
                    return augco - augc;
                if (augc == 0)
                    break;
            }
            mind = min(mind, d[E[xy].to]);//找与自己相连的距离标号的最小值
        }
    if (augco == augc) {//如果你不能从now走到任何一个点,就更新这个now的d值
        vd[d[x]]--;
        if (!vd[d[x]])//如果断层了,那么就可以结束。因为之改变了d[i]的值,就可以只判断d[i]
            d[1] = cnt;
        d[x] = mind + 1, vd[d[x]]++;
    }
    return augco - augc;//向上一层返回你在这个节点所能增广的量
}
int sap() {
    memset(d, 0, sizeof(d)), sizeof(vd, 0, sizeof(vd));
    vd[0] = cnt;
    int ans = 0;
    while (d[1] < T) ans += aug(S, INF);//刚开始进起点的量是无限大
    return ans;
}
  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值