USACO 4.2 Drainage Ditches 网络流SAP算法

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.



你可能感兴趣的:(USACO 4.2 Drainage Ditches 网络流SAP算法)