网络流初步——sap算法

网路流初步

什么是网络流?
请看下图:
这里写图片描述
这是一个有向图,每一条边都对应着两个数值“/”前面的称为流量,用 f(u,v) 表示,后面的称为容量,用 c(u,v) 表示,网络中有两个特殊的点,源点s和汇点t。

三个性质

(一)容量限制,对于所有原图中的变,均有 f(u,v)c(u,v)
(二)反对称性,为了方便计算,我们定 f(u,v)f(v,u) 中最多只有一个是正数,可以都为负数,这样定义的话,从u往v运三个物品,又从v往u运两个,就相当于只从u往v运了一个。
(三)留守恒性,从源点流出的流量一定等于流入t的容量。

最大流问题

定义一个网络的流量为 vVf(s,v) ,最大流问题就是在满足三个性质的条件下,最大化 f

残量网络

为了方便算法的实现,我们定义一个残量网络,残量网络的容量r(u,v)=c(u,v)f(u,v),通俗的讲,就是现在还能有多少流量经过。

增广路

在一条从s到t的路线中,对于在这条路线中任意一条弧都有 r(u,v)>0 ,那么这就是

增广路算法

每次我们找一条增广路,并记录下在这些残量网络中最小的流量,然后将这些在这条增广路中的残量网络减去这个最小值,总流量加上这个值,同时也是满足这三个性质的,就像这样一直找,找到没有增广路为止。

举个例子

假设这是原图:
这里写图片描述
我们要求这个图的最大流,首先找一条增广路, sv1v2t,flow+min{9,10,8} ,此时flow=8,然后原图就变成下面这个样子:
这里写图片描述
然后我们又找到一条增广路, sv1v2v3v4t,flowmin{(98),(108),8,7,4} ,此时flow=9。
这里写图片描述
接着再找一条增广路, sv3v4t ,flow加上 min{6,(71),(41)} ,此时flow=10。
此时在图中不存在增广路,算法结束。

最短增广路算法

每一次也是找一条增广路,只不过是最短的,其实时间复杂度基本是因为找增广路的时间,如果将找增广路的时间复杂度降下来,就好了。

距离标号

所谓距离标号,就是指某个点到汇点的经过的最少的弧的数量,设i点的距离标号为 di ,那么满足 di=dj+1 的(i,j)弧叫作允许弧,且增广时只走允许弧,那么无论怎么走都是最短路。初始化距离标号可以赋值为0。如何维护这个距离标号?
当在增广过程中发现从某点出发没有允许弧,那么就将这个点的距离标号设为从它出发的所有的弧的距离标号的最小值加1。

当前弧优化

对于每一个点我们都保存它的“当前弧”:初始化为第一条边,查找时直接从当前弧开始寻找,找到一条允许弧,就把它设为当前弧,改变距离标号的时候,将当前弧设为提供了最小标号的弧。

GAP优化

当一次距离标号时,出现了断层,那么就不存在增广路。

提醒

别看算法似乎很简单,有很多细节。

代码

now[i]表示当前弧。

const
        maxn=1000+5;
var
        flow,aug,i,j,k,a,b,c,n,m,tmp,min:longint;
        pre,g,vh,now,dis:array[0..maxn] of longint;
        h:array[0..maxn,0..maxn] of longint;
        bz:boolean;
begin
        readln(m,n);
        for i:=1 to m do
        begin
                readln(a,b,c);
                inc(h[a,b],c);
        end;
        for i:=1 to n do
                now[i]:=1;
        vh[0]:=n;
        aug:=maxlongint;
        i:=1;
        while dis[1]<n do
        begin
                bz:=false;
                g[i]:=aug;
                for j:=now[i] to n do
                begin
                        if (h[i,j]>0) and (dis[j]+1=dis[i]) then
                        begin
                            now[i]:=j;
                                bz:=true;
                                if h[i,j]<aug then
                                        aug:=h[i,j];
                                pre[j]:=i;
                                i:=j;
                                if i=n then
                                begin
                                        inc(flow,aug);
                                        while i<>1 do
                                        begin
                                                dec(h[pre[i],i],aug);
                                                inc(h[i,pre[i]],aug);
                                                i:=pre[i];
                                        end;
                                        aug:=maxlongint;
                                end;
                                break;
                        end;
                end;
                if bz then
                        continue;
                min:=n-1;
                for j:=1 to n do
                begin
                        if (h[i,j]>0) and (dis[j]<min) then
                        begin
                                k:=j;
                                min:=dis[j];
                        end;
                end;
                now[i]:=k;
                dec(vh[dis[i]]);
                if vh[dis[i]]=0 then
                        break;
                dis[i]:=min+1;
                inc(vh[dis[i]]);
                if i<>1 then
                begin
                        i:=pre[i];
                        aug:=g[i];
                end;
        end;
        writeln(flow);
end.

the end

因为我水平有限,可能会有写错的地方,希望大家批评指正,多多包容,thank you for your patience.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值