最大流:基础

前几个月第一次模仿别人的C代码半懂不懂得写了一次裸的最大流:RQNOJ 194:学生运输,算法导论上太多定义、分析、证明了,刚开始看得不是很明白,相比之下数据结构与算法分析有图解,但是没有给出代码。

后来才知道自己写的BFS增广是叫Edmonds-Karp算法,属于Ford-Fulkerson方法,效率是比DFS增广好些,但是也就只能对付RQ上的弱数据,碰到强题还是不行。

最近兴致突发想学习网络流,想学一下dinic,看王欣上的课件模模糊糊有些懂了,但是没有找到适合模仿的代码。

之后找到一份比较全的网络流资料,不但有dinic的分析还有SAP的课件和代码。然后看到zkw的SAP算法心得上讲“事实上,SAP算法更易于理解,时间效率更高,编程更简单,思路更清晰。”,给出的用SAP做USACO中的ditch的代码比我的EK都短,然后就背叛dinic了转去学SAP了。

SAP有当前弧和GAP优化,目前没有尝试过当前弧。

早上做NOI2006最大获利,思路很巧妙,黑书P317有几乎一样的题目的分析。具体的涉及最大闭权子图什么的,自己目前没有能力独立想出来,暂时先放一下吧。

我的RQNOJ194代码:

var
  a:array[1..1000,1..1000] of longint;
  q,prev,low:array[1..100000] of longint;
  m,n,max:longint;
procedure init;
var
  i,j,x,y,z:longint;
begin
  readln(m,n);
  for i:=1 to m do
  begin
    read(x,y,z);
    a[x,y]:=z;
  end;
end;
procedure max_flow;
var
  i,j,k,x,head,tail,s,t:longint;
begin
  while true do
  begin
    fillchar(low,sizeof(low),0);
   // fillchar(prev,sizeof(prev),0); I don't konw why can't=0 and change  (prev[i]<0)to<>0?
    for i:=1 to n do
     prev[i]:=-1;
    head:=1;
    tail:=1;
    q[head]:=1;
    s:=1;
    t:=n;
    low[1]:=maxint;
    while head<=tail do
    begin
      x:=q[head];
      inc(head);
      for i:=1 to n do
        if (a[x,i]>0)and(prev[i]<0) then
        begin
          inc(tail);
          q[tail]:=i;
          if low[x]<a[x,i] then
            low[i]:=low[x]
          else low[i]:=a[x,i];
          prev[i]:=x;
        end;
      if prev[t]>0 then break;
    end;
    if prev[t]>0 then
    begin
      x:=t;
      inc(max,low[t]);
      while x<>s do
      begin
        a[x,prev[x]]:=a[x,prev[x]]+low[t];
        a[prev[x],x]:=a[prev[x],x]-low[t];
        x:=prev[x];
      end;
    end else break;
  end;
end;

procedure print;
begin
  writeln(max);
end;

//=============main=================
begin
  init;
  max_flow;
  print;
end.


我的最大获利代码(80分)

const
  oo=19930508;//?
var
  vh,h,g,benefit,cost,e,c,opp,next:array[0..400000] of longint;
  augc,s,t,i,j,k,m,n:longint;
  flow,tmp,size,x,y,z,tot:longint;
  found:boolean;

procedure addedge(x,y,z:longint);
begin
  inc(size); e[size]:=y; next[size]:=g[x];
  g[x]:=size; opp[size]:=size+1;  c[size]:=z;
  inc(size); e[size]:=x; next[size]:=g[y];
  g[y]:=size; opp[size]:=size-1;  c[size]:=0;
end;

procedure init;
begin
  readln(n,m);        //m:user n:station
  for i:=1 to n do
    read(cost[i]);
  for i:=1 to m do
  begin
    readln(x,y,benefit[i]);
    addedge(i,m+x,oo);
    addedge(i,m+y,oo);
  end;
  s:=0;        //s:0 t:n+m+1 1..m:user m+1..m+n:station
  t:=m+n+1;
  tot:=t+1;
  for i:=1 to n do
    addedge(m+i,t,cost[i]);
  for i:=1 to m do
    addedge(s,i,benefit[i]);
end;
procedure sap(m:longint);
var
  minh,p,augco,i:longint;
begin
  minh:=tot-1;
  augco:=augc; //backup now augc
  if m=t then
  begin
    found:=true;
    inc(flow,augc);
    exit;      //found
  end;
  p:=g[m];
  while p>0 do
  begin
    i:=e[p];
    if c[p]>0 then
    begin
      if h[i]+1=h[m] then//can
      begin
        if c[p]<augc then
          augc:=c[p];
        sap(i);         //dfs
        if h[s]>=tot then exit; //no way
        if found then break;  //found
        augc:=augco;          //recover the augc
      end;
      if h[i]<minh then minh:=h[i];//adjustment
    end;
    p:=next[p];
  end;
  if not found then
  begin
    dec(vh[h[m]]);       //gap
    if vh[h[m]]=0 then h[s]:=tot;
    h[m]:=minh+1;
    inc(vh[h[m]]);
  end
  else begin
    dec(c[p],augc);
    inc(c[opp[p]],augc);
  end;
end;
procedure main;
begin
  vh[0]:=tot;
  while h[0]<tot do
  begin
    augc:=oo;
    found:=false;
    sap(0);
  end;
end;
procedure print;
begin
  for i:=1 to m do
    inc(tmp,benefit[i]);
  writeln(tmp-flow);
end;

begin
  assign(input,'profit.in');
  reset(input);
  assign(output,'profit.out');
  rewrite(output);

  init;
  main;
  print;

  close(output);
  close(input);
end.

因为是模仿别人的,很不理解为什么总节点数是t+1,其实是源点设成0了。增广的过程少打了一个局部变量,DEBUG了一个多小时。很疑惑为什么这个程序能过所有点(不过他数据范围似乎开小了),我和他程序的区别是:我用一个found函数记录有没有找到增广路,而他则是根据返回的增光路流量是不是为0。根据他的程序我改了一下,能AC:

const
  oo=19930508;//?
var
  vh,h,g,benefit,cost,e,c,opp,next:array[0..400000] of longint;
  augc,s,t,i,j,k,m,n:longint;
  flow,tmp,size,x,y,z,tot:longint;
  found:boolean;

procedure addedge(x,y,z:longint);
begin
  inc(size); e[size]:=y; next[size]:=g[x];
  g[x]:=size; opp[size]:=size+1;  c[size]:=z;
  inc(size); e[size]:=x; next[size]:=g[y];
  g[y]:=size; opp[size]:=size-1;  c[size]:=0;
end;

procedure init;
begin
  readln(n,m);        //m:user n:station
  for i:=1 to n do
    read(cost[i]);
  for i:=1 to m do
  begin
    readln(x,y,benefit[i]);
    addedge(i,m+x,oo);
    addedge(i,m+y,oo);
  end;
  s:=0;        //s:0 t:n+m+1 1..m:user m+1..m+n:station
  t:=m+n+1;
  tot:=t+1;
  for i:=1 to n do
    addedge(m+i,t,cost[i]);
  for i:=1 to m do
    addedge(s,i,benefit[i]);
end;

function sap(i,now:longint):longint;
var
  p,minh,tmp,j:longint;
begin
  minh:=tot-1;
  sap:=0;
  if i=t then
  begin
    inc(flow,now);
    exit(now);
  end;
  p:=g[i];
  while p>0 do
  begin
    j:=e[p];
    if c[p]>0 then
    begin
      if h[j]+1=h[i] then
      begin
        if c[p]<now then tmp:=sap(j,c[p]) else tmp:=sap(j,now);
        dec(now,tmp);
        inc(sap,tmp);
        dec(c[p],tmp);
        inc(c[opp[p]],tmp);
        if h[s]>=tot then exit;
        if now=0 then break;
      end;
      if h[j]<minh then minh:=h[j];
    end;
    p:=next[p];
  end;
  if sap=0 then
  begin
    dec(vh[h[i]]);
    if vh[h[i]]=0 then h[s]:=tot;
    h[i]:=minh+1;
    inc(vh[h[i]]);
  end;
end;    
procedure work;
begin
  vh[0]:=tot;
  while h[s]<tot do
    sap(s,oo);
end;

procedure print;
begin
  for i:=1 to m do
    inc(tmp,benefit[i]);
  writeln(tmp-flow);
end;

begin
  assign(input,'profit.in');
  reset(input);
  assign(output,'profit.out');
  rewrite(output);

  init;
  work;
  print;

  close(output);
  close(input);
end.


posted on 2011-05-22 11:32  oa414  阅读(295)  评论(0编辑  收藏  举报

导航