题目:TYVJ1338

题解:

根据奇偶格子,可以表示成一张二分图,然后相邻格子之间连边。相当于是在一张二分图里面选出权值和尽量大的点使得任两个选出来的点不相邻。考虑所有没有选出来的点,那些点恰好构成了一个二分图点对边的覆盖,并且使得权值和尽量小。


格子黑白染色后。
建立源点s,s到每个黑格连条弧,容量为格子的权值。
建立汇点t,每个白格到t连条弧,容量为格子的权值。
如果某两格子有公共边,就从黑格到白格连一条容量为∞的弧
做s到t的最大流。
答案即为(sum - maxflow)
sum为总权值。

个人乱七八糟的理解:

二分图中,流图的点都是不选的,如果还有增广路就流过去了。最大流=最小割,所以最大流量=不选的点的权值和,sum-MaxFlow即为解。

一点体会:

发现自己构图什么的还能一次搞定,开始时算出的流量总是0,以为构图错了,然后输出每条边自己模拟,发现是对的。最终发现是sap过程中把C[p]>0写成C[p]<0了……

我的SAP实现还算可以,TYVJ上的题解有说要加CUR优化的,有只用GAP优化但是跑到1493ms的,我的只是GAP优化但是670ms就搞出来了。

贴代码:

const
  oo=19930508;
var
  h,vh,c,opp,next,e,g:array[0..1000000] of longint;
  i,j,k,m,n,s,t,sum,p,maxflow,v,size,w,tot:longint;
  pos:array[-1..1000,-1..1000] of longint;


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

function min(a,b:longint):longint;
begin
  if a<b then exit(a)else exit(b);
end;

function sap(i,now:longint):longint;
var
  minh,j,tmp,p:longint;
begin
  minh:=tot-1;
  sap:=0;
  if i=t then
  begin
    inc(maxflow,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
        tmp:=sap(j,min(now,c[p]));
        dec(c[p],tmp);
        inc(c[opp[p]],tmp);
        dec(now,tmp);
        inc(sap,tmp);
        if h[s]>=tot then exit;
        if now=0 then break;
      end;
      minh:=min(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 init;
begin
  readln(n);
  s:=n*n+1;
  t:=s+1;
  for i:=1 to n do
    for j:=1 to n do
    begin
      inc(v);
      pos[i,j]:=v;
      read(w);
      inc(sum,w);
      if odd(i) xor odd(j) then
        addedge(s,v,w)
      else addedge(v,t,w);
    end;
  v:=v+2;
  tot:=v;
  for i:=1 to n do
    for j:=1 to n do
    if odd(i) xor odd(j) then
    begin
      if pos[i+1,j]<>0 then addedge(pos[i,j],pos[i+1,j],oo);
      if pos[i-1,j]<>0 then addedge(pos[i,j],pos[i-1,j],oo);
      if pos[i,j+1]<>0 then addedge(pos[i,j],pos[i,j+1],oo);
      if pos[i,j-1]<>0 then addedge(pos[i,j],pos[i,j-1],oo);
    end;
 { for i:=1 to v do
  begin
    p:=g[i];
    while p<>0 do
    begin
      writeln(i,' --> ',e[p],'  ',c[p]);
      p:=next[p]
    end;
  end;  }
end;

procedure aug;
begin
  vh[0]:=tot;
  while h[s]<tot do
    sap(s,oo);
end;

procedure print;
begin
  writeln(sum-maxflow);
end;


begin
  init;
  aug;
  print;
end.



相关文章: