zkw是一个求最小费用最大流的一种又短又快的算法,很多情况下,虐暴spfa。
不过不能有负费用或0。
zkw运用了gap的分层思想,不过他是用费用分层的。
设一个d[i]表示i到t的最短距离,bz[i]表示现在第i个点得标记(标记id时刻++),now[i]的当前弧(sap时代替first,然后时刻更新,很显然)。
不过有一个问题,d[i]既然是i到t的距离,但是后面的点还没有搜过,如何知道d[i]。
好了,其实d[i]在网络流结束后才是i到t的最短距离。目前的意思是,i扩展到最新的一层是,到最近点得距离。
如何更新这个距离,每次用o=min(o,d[last[i]]-d[x]+cost[i]),然后所有已经搜过的点的d都加上o。sap判断的时候如果d[x]=d[last[i]]+cost[i],那么就走last[i]这个点。
现在问题又来了,在更新的时候,d[last[i]]等于0怎么办。
其实没有关系,在走点判断的时候,因为d[x]已经加过o了,所以等式的d[x]会因为o而抵消,非常巧妙。在这之后d[x]的值就会变正常。
zkw也可以求最大流,把所有费用射为1就可以了,为0就无法扩展了,因为0+0=0,但是1+1=2。
int zkw(int x,int y,int z){
int i,j,k,l;
if(y==0)return 0;
if(z<0){
i=i;
}
if(x==n+1){ans+=y*z;return y;}
bz[x]=id;
for(i=now[x];i;i=next[i]){
if(bz[last[i]]!=id&&flow[i]&&d[x]==d[last[i]]+cost[i]){
k=zkw(last[i],min(y,flow[i]),cost[i]+z);
if(k){
flow[i]-=k;
flow[fan[i]]+=k;
now[x]=i;
return k;
}
}
}
now[x]=0;
return 0;
}
bool pan(){
int i,j,k,o=inf;
fo(i,0,n+1){
if(bz[i]==id){
for(j=first[i];j;j=next[j]){
if(bz[last[j]]!=id&&flow[j])o=min(o,d[last[j]]-d[i]+cost[j]);
}
}
}
if(o==inf)return 1;
fo(i,0,n+1){
if(bz[i]==id){
d[i]+=o;
}
}
return 0;
}
do{
++id;
fo(i,0,n+1)now[i]=first[i];
while(zkw(0,inf,0)>0)++id;//注意,这里要增广完为止
}while(!pan());