最大流:基础

前几个月第一次模仿别人的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.





你可能感兴趣的:(最大流)