ZKW,SPFA费用流模板

39 篇文章 6 订阅
10 篇文章 0 订阅

费用流比较常用的做法有两种:SPFA费用流和ZKW费用流

两种费用流的基本做法相同:找到费用最小的路,不断增广到不能增广为止
正确性很显然,因为每次都找费用最小的路,所以费用一定最小,因为增广到不能增广才停止,所以一定是最大流

SPFA

费用流打起来和理解起来很简单,就是通过SPFA找到费用最小的路,并把这条路上的每条边的流量都减去这整条路的流量最小值,不断循环直到找不到增广路位置
缺点也很显然:在稠密图中速度太慢

Code:

找不到C++的了,发一个几年前码的Pascal吧

function spfa:boolean;
var
    i,j,k,l:longint;
begin
    for i:=1 to t do begin f[i]:=-maxlongint+100000;bz[i]:=false;end;
    i:=0;j:=1;
    d[1]:=s;bz[s]:=true; f[s]:=0;
    while i<j do
    begin
        inc(i);
        l:=d[i];
        for k:=1 to t do
        begin
            if (a[l,k]>0)and(f[l]+c[l,k]>f[k]) then
            begin
                f[k]:=c[l,k]+f[l];
                from[k]:=l;
                if bz[k]=false then
                begin
                    bz[k]:=true;
                    inc(j);d[j]:=k;
                end;
            end;
        end;
        bz[l]:=false;
    end;
    exit(f[t]>0);
end;

begin
    while spfa do
    begin
        i:=t; mi:=maxlongint;
        while i<>s do
        begin
            j:=i;i:=from[i];
            mi:=min(mi,a[i,j]);
        end;
        i:=t;
        while i<>s do
        begin
            j:=i;i:=from[i];
            dec(a[i,j],mi);inc(a[j,i],mi);
            ans:=ans+c[i,j];
        end;
    end;
    writeln(ans);
end.

ZKW

这种做法略高级,理解起来有一点难度
它和SAP的做法有点像,SAP是只走高度相差为1的点,ZKW只走费用差最小的点
也就是说,ZKW中的费用相当于SAP中的高度
设dis[x]表示x到T的最短费用
假设有一条路从x->y
那么dis[x]-fee[x,y]=dis[y]
也就是每次在已经走的点中选一个高度(dis)升高最小的作为下次增广的边
我语文能力太差,直接上标吧
putin是连边

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,S,T,last[M],next[M*10],to[M*10],date[M*10],tot=1,bz[M],a[N][N];
db fee[M*10],dis[M];
void putin(int x,int y,int z,db f)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;fee[tot]=f,date[tot]=z;
    next[++tot]=last[y];last[y]=tot;to[tot]=x;fee[tot]=-f;date[tot]=0;
}
int dg(int x,int z)
{
    if(x==T) return z;
    int jy=0;bz[x]=1;
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if(!bz[y]&&date[i]&&abs(dis[y]-dis[x]+fee[i])<0.000001)
        {
            int qq=dg(y,min(date[i],z));
            jy+=qq,date[i]-=qq,date[i^1]+=qq,z-=qq;
            if(z==0) return jy;
        }
    }
    return jy;
}
bool change()
{
    db minh=INF;
    fo(x,1,T) 
    if(bz[x])
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if(!bz[y]&&date[i]>0)
        minh=min(minh,dis[y]-dis[x]+fee[i]);
    }
    if(abs(minh-INF)<0.000001) return 0;
    if(minh<-1) return 0;
    fo(x,1,T) if(bz[x]) dis[x]+=minh;
    return 1;
}
int main()
{
    do do
        memset(bz,0,sizeof(bz));
        while(dg(S,INF));
    while(change());
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值