费用流:方格取数

纠结这个问题已经很久了,题目不难,简单的DP就行了。可是也可以算是网络流建模的经典例题。今天终于调过了NOIp2008的传纸条,顺便把程序改了改交了NOIp2000的方格取数与3取方格数、n取方格数。

传纸条的算法见NOIP2008 传纸条 费用流建模

原来以为只有输入不同,但是传纸条与方格取数还是不同的,传纸条要求每个同学只能帮一次,所以拆点后连一条边即可,而方格取数可以重复经过(虽然数字已经被拿走了),所以还要连一条费用为0,容量为INF的边,才可以在选择之后重复经过。开始时没考虑到,WA了好多次。

关于方格取数,可以参考从2取方格数到N取方格数N取方格数,讲得都很好,就是代码恶心了点(好吧我承认我的代码也很丑)

3取方格数和N取方格数在tyvj上有,P1413和P1414。传纸条和方格取数都是NOIp的原题,到处都有。另外HDU上有方格取数的改编,有空就去把它搞掉吧。

发现不常练习真的是不行的。高考放假后到现在十几天没写费用流了,现在写个裸的费用流还花很长时间,还是抄自己原来代码的,还一堆bug……

另外写这种超过一百行的代码要规范些才好,我的代码还是很丑的,比如队列的操作可以放到过程里,等等。

附传纸条与N取方格数源程序,只是add_edge和init过程略有不同。

N取方格数:

const

  oo=19930317;





var

  e,c,next,opp,q,cost:array[0..100000] of longint;

  dist,g,pre,pre_edge:array[0..100000] of longint;

  x:array[0..100,0..100] of longint;

  flag:array[0..100000] of boolean;

  i,j,k,a,b:longint;

  max_cost:longint;

  s,t:longint;

  n,m:longint;

  size:longint;

  now,flow,p:longint;



function min(a,b:longint):longint;

begin

  if a<b then exit(a) else exit(b);

end;



procedure add_edge(x,y,z,w:longint);

begin

  inc(size); e[size]:=y; c[size]:=w; next[size]:=g[x];

  g[x]:=size;cost[size]:=z;opp[size]:=size+1;



  inc(size); e[size]:=x; c[size]:=0; next[size]:=g[y];

  g[y]:=size;cost[size]:=-z;opp[size]:=size-1;

end;





function spfa:boolean;

var

  head,tail,i,j,p:longint;

begin

  head:=0;

  tail:=1;

  q[1]:=s;

  for i:=1 to t do

    dist[i]:=-oo;

  dist[s]:=0;

  fillchar(flag,sizeof(flag),0);

  flag[s]:=true;

  while head<tail do

  begin

    inc(head);

    i:=q[head];

    flag[i]:=false;

    p:=g[i];

    while p<>0 do

    begin

      j:=e[p];

      if (dist[i]+cost[p]>dist[j])and(c[p]>0) then

      begin

        dist[j]:=cost[p]+dist[i];

        pre[j]:=i;

        pre_edge[j]:=p;

        if not flag[j] then

        begin

          inc(tail);

          q[tail]:=j;

          flag[j]:=true;

        end;

      end;

      p:=next[p];

    end;

  end;

  exit(dist[t]<>-oo);

end;





procedure init;

begin

  readln(k,m,n);

  j:=1;

  for i:=1 to n do

    for j:=1 to m do

      read(x[i,j]);

  for i:=1 to n do

    for j:=1 to m do

    begin

      a:=m*(i-1)+j;

      b:=a*2;

      a:=b-1;

      add_edge(a,b,x[i,j],1);

      add_edge(a,b,0,oo);

    end;

  for i:=1 to n do

  begin

    for j:=1 to m do

    begin

      a:=(m*(i-1)+j)*2;

      if j<m then

        add_edge(a,((i-1)*m+j+1)*2-1,0,oo);

      if i<n then

        add_edge(a,((i*m)+j)*2-1,0,oo);

    end;

  end;

  s:=n*m*2+1;

  t:=s+1;

  add_edge(s,1,0,oo);

  add_edge(n*m*2,t,0,k);

end;



procedure main;

begin

  while spfa do

  begin

    now:=t;

    flow:=oo;

    while now<>s do

    begin

      flow:=min(flow,c[pre_edge[now]]);

      now:=pre[now];

    end;

    now:=t;

    while now<>s do

    begin

      p:=pre_edge[now];

      dec(c[p],flow);

      inc(c[opp[p]],flow);

      inc(max_cost,cost[p]*flow);

      now:=pre[now];

    end;

  end;

end;





begin

  init;

  main;

  writeln(max_cost);

end.



传纸条:

const

  oo=19930317;





var

  e,c,next,opp,q,cost:array[0..100000] of longint;

  dist,g,pre,pre_edge:array[0..100000] of longint;

  flag:array[0..100000] of boolean;

  i,j,k,a,b,x:longint;

  max_cost:longint;

  s,t:longint;

  n,m:longint;

  size:longint;

  now,flow,p:longint;



function min(a,b:longint):longint;

begin

  if a<b then exit(a) else exit(b);

end;



procedure add_edge(x,y,z:longint);

begin

  inc(size); e[size]:=y; c[size]:=1; next[size]:=g[x];

  g[x]:=size;cost[size]:=z;opp[size]:=size+1;



  inc(size); e[size]:=x; c[size]:=0; next[size]:=g[y];

  g[y]:=size;cost[size]:=-z;opp[size]:=size-1;

end;





function spfa:boolean;

var

  head,tail,i,j,p:longint;

begin

  head:=0;

  tail:=1;

  q[1]:=s;

  for i:=1 to t do

    dist[i]:=-oo;

  dist[s]:=0;

  fillchar(flag,sizeof(flag),0);

  flag[s]:=true;

  while head<tail do

  begin

    inc(head);

    i:=q[head];

    flag[i]:=false;

    p:=g[i];

    while p<>0 do

    begin

      j:=e[p];

      if (dist[i]+cost[p]>dist[j])and(c[p]>0) then

      begin

        dist[j]:=cost[p]+dist[i];

        pre[j]:=i;

        pre_edge[j]:=p;

        if not flag[j] then

        begin

          inc(tail);

          q[tail]:=j;

          flag[j]:=true;

        end;

      end;

      p:=next[p];

    end;

  end;

  exit(dist[t]<>-oo);

end;





procedure init;

begin

  readln(n,m);

  for i:=1 to n do

    for j:=1 to m do

    begin

      read(x);

      a:=m*(i-1)+j;

      b:=a*2;

      a:=b-1;

      add_edge(a,b,x);

    end;

  c[1]:=2;

  c[size-1]:=2;

  for i:=1 to n do

  begin

    for j:=1 to m do

    begin

      a:=(m*(i-1)+j)*2;

      if j<m then

        add_edge(a,((i-1)*m+j+1)*2-1,0);

      if i<n then

        add_edge(a,((i*m)+j)*2-1,0);

    end;

  end;

  s:=1;

  t:=n*m*2;

end;



procedure main;

begin

  while spfa do

  begin

    now:=t;

    flow:=oo;

    while now<>s do

    begin

      flow:=min(flow,c[pre_edge[now]]);

      now:=pre[now];

    end;

    now:=t;

    while now<>s do

    begin

      p:=pre_edge[now];

      dec(c[p],flow);

      inc(c[opp[p]],flow);

      inc(max_cost,cost[p]*flow);

      now:=pre[now];

    end;

  end;

end;





begin

  init;

  main;

  writeln(max_cost);

end.

你可能感兴趣的:(流)