连续研究了几天的费用流,一个属于自己版本的zkw费用流出炉了。
zkw神牛发明的zkw算法可以说是编写简单,速度也非常不错。
个人还给zkw加了一个当前弧优化,KM算法中的松弛变量优化与连续增广优化,不过加了这些优化后zkw的全部过程也只有30行。
感觉上zkw就是优化了spfa求最短路的过程,因为spfa没有充分利用上次求出来的结果最短路的结果,
zkw相当于迪杰斯特拉每次加入一条介于访问过的点和没有访问过的点之间一条改进量最小的边,所谓的改进量就是g[k]=dis[i]-dis[j]+w[k];
关于一开始就有负权的边,ZKW牛本人说先求一次可行流再求一次最小费用流,这个我没看懂。。大量随机数据证明,只有正权的费用流用zkw是不会错的,那么根据题目的性质给负权的边加上一个极大值就可以了。
不知道为什么(难道是我随出来的数据太水了),zkw在各种图上有无法超越的优势,随机稠密图比spfa快10倍左右,300个点50000条边大约1.5s比spfa快8倍左右,10000个点100000条边12s左右,spfa一分钟没跑出来不想等了。
经典费用流napkin:
program ex10; var o,v:array[0..1600] of boolean; f,s,d,dis:array[0..1600] of longint; next,p,c,w:array[-20000..20000] of longint; n,i,j,t,tt,ft,ff,st,sf,ans,imp,new,flow:longint; procedure link(i,j,k,l:longint); begin inc(t);next[t]:=d[i];d[i]:=t;p[t]:=j;c[t]:=k;w[t]:=l; next[-t]:=d[j];d[j]:=-t;p[-t]:=i;w[-t]:=-l; end; function dfs(i,flow:longint):longint; var j,k,l,min:longint; begin if i=tt then begin inc(ans,dis[i]*flow);exit(flow); end; k:=s[i];j:=p[k];dfs:=0; o[i]:=true;v[i]:=true; while k<>0 do begin l:=dis[i]+w[k]-dis[j];min:=flow; if c[k]<min then min:=c[k]; if (min>0)and(l<f[j]) then f[j]:=l; if (min>0)and(l=0)and not o[j] then begin l:=dfs(j,min); inc(dfs,l);dec(flow,l); dec(c[k],l);inc(c[-k],l); end; if flow=0 then break; s[i]:=next[s[i]]; k:=s[i];j:=p[k]; end; o[i]:=false; end; begin assign(input,'input.txt');reset(input); assign(output,'output.txt');rewrite(output); readln(n,new,ft,ff,st,sf);tt:=2+2*n; for i:=1 to n do begin read(j); link(1,i+1+n,1<<20,new); link(1,i+1,j,0);link(i+1+n,tt,j,0); if i<>n then link(i+1,i+2,1<<20,0); if i+ft<=n then link(i+1,i+ft+1+n,1<<20,ff); if i+st<=n then link(i+1,i+st+1+n,1<<20,sf); end; repeat for i:=1 to tt do s[i]:=d[i]; fillchar(v,sizeof(v),false); fillchar(f,sizeof(f),1); inc(flow,dfs(1,1<<20)); imp:=maxint; for i:=1 to tt do if not v[i] and (f[i]<imp) then imp:=f[i]; for i:=1 to tt do if not v[i] then inc(dis[i],imp); until imp=maxint; writeln(ans); close(output);close(input); end.