费用流:方格取数

纠结这个问题已经很久了,题目不难,简单的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.

posted on 2011-06-23 23:17  oa414  阅读(840)  评论(0编辑  收藏  举报

导航