NOI2009 植物大战僵尸 PVZ

和NOI2006的最大获利一样,都是最大权闭合图的应用。可参考胡伯涛的论文。

但这道题还需在建图时去除环。因为最大权闭合图算法不能判断环的情况。

type
  edge=record
         x,y,f,next,op:longint;
       end;
const oo=10000000;
var
  e:array[1..500000] of edge;
  no:array[1..80,1..80] of longint;
  map:array[0..605,0..605] of boolean;
  a,g,h,d,p,num,now:array[0..10000] of longint;
  v:array[0..4000] of boolean;
  i,j,k,m,n,tot,s,t,x,y,z,sum:longint;
procedure add(x,y,f:longint);
  begin
    inc(tot);
    e[tot].x:=x;e[tot].y:=y;
    e[tot].f:=f;
    e[tot].next:=h[x];h[x]:=tot;
    inc(tot);
    e[tot].x:=y;e[tot].y:=x;
    e[tot].f:=0;
    e[tot].next:=h[y];h[y]:=tot;
    e[tot].op:=tot-1;e[tot-1].op:=tot;
  end;
function isap:longint;
  var u,v,i,j,tmp,flow,aug:longint;ff:boolean;
  begin
    flow:=0;aug:=oo;
    for i:=0 to t+1 do
    begin
      d[i]:=0;p[i]:=-1;
      num[i]:=0;now[i]:=h[i];
    end;
    u:=s;num[0]:=t+1;
    while d[s]<t+1 do
    begin
      ff:=false;i:=now[u];
      while i<>0 do
      begin
        v:=e[i].y;
        if (e[i].f>0)and(d[u]=d[v]+1) then
        begin
          ff:=true;p[v]:=i;
          if e[i].f<aug then aug:=e[i].f;
          now[u]:=i;u:=v;
          if u=t then
          begin
            flow:=flow+aug;
            while u<>s do
            begin
              j:=p[u];
              dec(e[j].f,aug);
              inc(e[e[j].op].f,aug);
              u:=e[j].x;
            end;
            aug:=oo;
          end;
          break;
        end;
        i:=e[i].next;
      end;
      if ff then continue;
      dec(num[d[u]]);
      if num[d[u]]=0 then exit(flow);
      tmp:=t+1;i:=h[u];
      while i>0 do
      begin
        v:=e[i].y;
        if (e[i].f>0)and(d[v]<tmp) then
        begin
          tmp:=d[v];now[u]:=i;
        end;
        i:=e[i].next;
      end;
      d[u]:=tmp+1;
      inc(num[d[u]]);
      if u<>s then u:=e[p[u]].x;
    end;
    exit(flow);
  end;
procedure toposort;
  var i,j,k,x,y:longint;
  begin
    fillchar(v,sizeof(v),0);
    repeat
      k:=-1;
      for i:=1 to n*m do
      if a[i]=0 then
      begin
        k:=i;break;
      end;
      if k=-1 then break;
      a[k]:=-1;v[k]:=true;
      for i:=1 to n*m do
        if map[k,i] and not v[i] then dec(a[i]);
    until false;
    for i:=1 to n*m do
      for j:=1 to n*m do
      if v[i] and v[j] and map[i,j] then
        add(j,i,oo);
    for i:=1 to n do
      for j:=1 to m do
      if v[no[i,j]] then
      begin
        k:=g[no[i,j]];
        if k>0 then
        begin
          add(s,no[i,j],k);
          sum:=sum+k;
        end;
        if k<0 then
          add(no[i,j],t,-k);
      end;
  end;
begin
  assign(input,'pvz10.in');reset(input);
  readln(n,m);
  for i:=1 to n do
    for j:=1 to m do
    no[i,j]:=(i-1)*m+j;
  s:=0;t:=n*m+1;
  for i:=1 to n do
    for j:=1 to m do
    begin
      read(g[no[i,j]]);
      read(k);
      for z:=1 to k do
      begin
        read(x,y);inc(x);inc(y);
        if not map[no[i,j],no[x,y]] then
        begin
          map[no[i,j],no[x,y]]:=true;
          inc(a[no[x,y]]);
        end;
      end;
      readln;
    end;
  for i:=1 to n do
    for j:=2 to m do
    if not map[no[i,j],no[i,j-1]] then
    begin
      map[no[i,j],no[i,j-1]]:=true;
      inc(a[no[i,j-1]]);
    end;
  toposort;
  writeln(sum-isap);
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值