网络流Sap+Gap(【USACO题库】4.2.1 Drainage Ditches草地排水 )

44 篇文章 0 订阅
6 篇文章 0 订阅

网络流-Dinic在此

这里写图片描述

网络流的定义 & 概念 & 残余网络

没错这就是上面的链接
不多讲

Sap

其实Sap的核心思想和Dinic相同,都是通过编号来减少搜索次数
只不过,Sap效率(+Gap),代码复杂度都较小。

定义d[i]表示点i到汇点最少经过的弧数量。称作距离标号。
则弧(i,j)为允许弧的情况就是
d[i]=d[j]+1
每次走只走允许弧,这样效率会快很多。
这里写图片描述
经过处理距离标号后,从2就不会走到3,这样效率会快很多。

处理距离标号

可以通过反向BFS,从汇点往源点走,以此计算出距离标号。
不过这样做,需要把路径转向,比较麻烦。

实际上,可以一开始把所有距离标号都设为0,在寻找的过程中可以通过更新来更改,不会影响到正确性。

更新标号

每次查找完路径后,如果没有找到流,就把路径上的所有点更改距离标号。
d[i]=min(d[j])+1 (i,j间存在有流量的路径)

★Gap优化★(重点)

可以得知,距离标号的更改一定是单调递增的。
每一次重标号后,有可能标号间会出现断层(某一种标号不存在)
所以这个时候一定就没有路径到达汇点。

代码

var
        s:array[1..200,1..200] of longint;
        f:array[1..200] of longint;
        gap:array[0..200] of longint;
        n,m,i,j,k,l,x,y,z,ans,sum:longint;
        find:boolean;
function min(x,y:longint):longint;
begin
        if x<y then min:=x
        else
        min:=y;
end;
procedure dfs(t:longint);
var
        i,j,k,mn:longint;
begin
        k:=sum;
        if t=m then//找到流
        begin
                find:=true;
                ans:=ans+sum;
                exit;
        end;
        mn:=m-1;//如果没找到路径那么该点就不可行
        for i:=1 to m do
        if s[t,i]>0 then//存在流量
        begin
                if f[t]=f[i]+1 then//允许弧
                begin
                        if s[t,i]<sum then//计算流量
                        sum:=s[t,i];
                        dfs(i);
                        if find then//找到即退出
                        break;
                end;
                sum:=k;//计算流量
                mn:=min(mn,f[i]);//重标号
        end;
        if find=false then
        begin
                dec(gap[f[t]]);//Gap
                if gap[f[t]]=0 then//Gap
                begin
                        writeln(ans);
                        halt;
                end;
                f[t]:=mn+1;//重标号
                inc(gap[f[t]]);//Gap
        end
        else
        begin//反向弧
                dec(s[t,i],sum);
                inc(s[i,t],sum);
        end;
end;
begin
        readln(n,m);
        for i:=1 to n do
        begin
                readln(x,y,z);
                s[x,y]:=s[x,y]+z;
        end;
        gap[0]:=m;
        while f[1]<m do//直到不存在路径为止
        begin
                find:=false;
                sum:=maxlongint;
                dfs(1);
        end;
        writeln(ans);
end.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值