SAP同样是效率很高的一个算法,与dinic想比,他只需要两种优化,就能达到dinic在DFS里面加一大堆优化的效率。下面描述该算法的流程:
首先给每个点赋予一个距离标号:d[i]。只有满足d[i]=d[j]+1时,边(i,j)才被视为存在的(暂且称为容许边)。这个标号的意义是到汇点的最短距离。
算法的一开始,我们从汇点进行一次BFS求出距离标号(实际上这一步可以省去)。然后从源点开始递归操作。
对于一个点i的操作:
如果存在容许边(i,j),当j是汇点时增广并从源点开始递归。否则就令i是j的前驱,并递归j。
如果不存在容许边,则对i重标号,使得d[i]=min{d[j]}+1 (w(i,j)>0),并回溯。(可以证明重标号操作必然是令d[i]增加的)
当d[s]>=n时算法停止,此时不存在任何一条增广路。
两个重要优化分别是当前弧优化和间隙优化。
当前弧优化:对于一个点i,如果之前递归到这里,并且已经枚举过一些不能增广的边,那么可以证明在这个点重标号之前,之前已无法增广的边不会再次成为容许边。所以记录每个点的出边枚举到那一条,下次直接从这一条开始枚举即可。
间隙优化:假如整个图中,不存在d[i]=k且存在d[i]>k和d[i]<k,那么可以退出整个算法了。因为该图已经不存在增广路了。
代码:
var t,l,n,m,e,ans:longint; lastside,d,num,fa,cur:array[0..2000] of longint; side:array[1..2000] of record x,y,c,next,op:longint; end; procedure add(x,y,c:longint); begin inc(e); side[e].x:=x; side[e].y:=y; side[e].c:=c; side[e].op:=e+1; side[e].next:=lastside[x]; lastside[x]:=e; inc(e); side[e].x:=y; side[e].y:=x; side[e].c:=0; side[e].op:=e-1; side[e].next:=lastside[y]; lastside[y]:=e; end; procedure init; var i,x,y,c:longint; begin readln(n,m); e:=0; fillchar(lastside,sizeof(lastside),0); for i:=1 to m do begin readln(x,y,c); add(x,y,c); end; end; procedure remark(x:longint); var min,i:longint; begin min:=n-1; cur[x]:=lastside[x]; i:=cur[x]; while i>0 do with side[i] do begin if (c>0)and(d[y]<min) then min:=d[y]; i:=next; end; d[x]:=min+1; end; procedure change; var i,min:longint; begin i:=n; min:=maxlongint; while i>1 do with side[fa[i]] do begin if c<min then min:=c; i:=x; end; ans:=ans+min; i:=n; while i>1 do with side[fa[i]] do begin dec(c,min); inc(side[op].c,min); i:=x; end; end; procedure main; var i:longint; begin fillchar(d,sizeof(d),0); fillchar(num,sizeof(num),0); fillchar(fa,sizeof(fa),0); for i:=1 to n do cur[i]:=lastside[i]; num[0]:=n; ans:=0; i:=1; while d[1]<n do begin while cur[i]>0 do with side[cur[i]] do if (c>0)and(d[y]+1=d[x]) then break else cur[i]:=next; if cur[i]=0 then begin dec(num[d[i]]); if num[d[i]]=0 then break; remark(i); inc(num[d[i]]); if i<>1 then i:=side[fa[i]].x; end else begin fa[side[cur[i]].y]:=cur[i]; i:=side[cur[i]].y; if i=n then begin change; i:=1; end; end; end; end; begin readln(t); for l:=1 to t do begin init; main; writeln('Case ',l,': ',ans); end; end.