1. 程式人生 > >ZKW,SPFA費用流模板

ZKW,SPFA費用流模板

費用流比較常用的做法有兩種: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());
}