网络流的定义 & 概念 & 残余网络
没错这就是上面的链接
不多讲
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.