题解:
根据奇偶格子,可以表示成一张二分图,然后相邻格子之间连边。相当于是在一张二分图里面选出权值和尽量大的点使得任两个选出来的点不相邻。考虑所有没有选出来的点,那些点恰好构成了一个二分图点对边的覆盖,并且使得权值和尽量小。
格子黑白染色后。
建立源点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.