原题
题目描述 Description
有一个N×M的单位方格中,其中有些方格是水塘,其他方格是陆地.如果要用1×2的矩阵区覆盖(覆盖过程不容许有任何部分重叠)这个陆地,那么最多可以覆盖多少陆地面积.
输入描述 Input Description
输入文件的第一行是两个整数N,M (1<=N,M<=100),第二行为一个整数K( K<=50),接下来的K行,每行两个整数X,Y表示K个水塘的行列位置。(1<=X<=N,1<=Y<=M)。
输出描述 Output Description
输出所覆盖的最大面积块(1×2面积算一块)。
样例输入 Sample Input
4 4
6
1 1
1 4
2 2
4 1
4 2
4 4
样例输出 Sample Output
4
题解
这道题一开始看以为是什么状压DP就一直没敢做,但最近周围各位神犇都在写这道题,就认真地再看了一次.
由于是求1*2或者2*1的块的个数,那就可以用二分图来做.
建图的时候就把相邻两个点连接起来.
每个点最多只能在二分图的左边出现一次,如果某个点在二分图的右边出现过一次,那么它同样不能加入二分图的左边.
由于我的算法是Dinic,因此要建一个原点与二分图左边的每个点一一相连.
同样的,二分图右边的每个点也要与汇点建一条边,但值得注意的是,二分图右边的点最多只能和汇点连一条边.
因此,我们就要建立两个数组usex/usey分别表示某一个点是否在二分图的左边或是右边出现,是为True,否为False.
建图之后直接套Dinic就OK了.
Code:
const fx:array[1..4] of longint=(1,-1,0,0);
const fy:array[1..4] of longint=(0,0,1,-1);
var ne,b,c,q:array[0..100000] of longint;
var fir,dis:array[0..1001] of longint;
var usex,usey:array[0..1001] of boolean;
var a:array[0..100,0..100] of boolean;
var i,j,n,m,tot,x,y,k,ans:longint;
function v(x,y:longint):longint; begin exit((x-1)*m+y); end;
procedure add(x,y:longint);
var ty,tx,i,xx,yy:longint;
begin
tx:=v(x,y);
usex[tx]:=true;
for i:=1 to 4 do
begin
xx:=x+fx[i];yy:=y+fy[i];
ty:=v(xx,yy);
if (not a[xx,yy])and(not usex[ty])and(xx<=n)and(xx>0)and(yy<=m)and(yy>0) then
begin
inc(tot);b[tot]:=tx;c[tot]:=1;ne[tot]:=fir[0];fir[0]:=tot;//左边的点与原点建边
inc(tot);b[tot]:=0;c[tot]:=0;ne[tot]:=fir[tx];fir[tx]:=tot;
inc(tot);b[tot]:=ty;c[tot]:=1;ne[tot]:=fir[tx];fir[tx]:=tot;//两点之间建边
inc(tot);b[tot]:=tx;c[tot]:=0;ne[tot]:=fir[ty];fir[ty]:=tot;
if not usey[ty] then//只能和汇点建一条边!!!只能和汇点建一条边!!!只能和汇点建一条边!!!
begin
inc(tot);b[tot]:=m*n+1;c[tot]:=1;ne[tot]:=fir[ty];fir[ty]:=tot;
inc(tot);b[tot]:=ty;c[tot]:=0;ne[tot]:=fir[n*m+1];fir[n*m+1]:=tot;
end;
usey[ty]:=true;
end;
end;
end;
procedure bfs;
var i,h,t,now:longint;
begin
for i:=0 to n*m+1 do dis[i]:=-1;
h:=1;t:=1;dis[0]:=0;q[1]:=0;
while h<=t do
begin
now:=q[h];j:=fir[now];
while j>0 do
begin
if (c[j]>0)and(dis[b[j]]=-1) then
begin
dis[b[j]]:=dis[now]+1;
inc(t);q[t]:=b[j];
end;j:=ne[j];
end;inc(h);
end;
end;
function dfs(x,mx:longint):longint;
var k,j:longint;
begin
if x=n*m+1 then exit(mx);j:=fir[x];
while j>0 do
begin
if (c[j]>0)and(dis[b[j]]=dis[x]+1) then
begin
if mx<c[j] then k:=dfs(b[j],mx) else k:=dfs(b[j],c[j]);
dec(c[j],k);
if odd(j) then inc(c[j+1],k) else inc(c[j-1],k);
if k>0 then exit(k);
end;j:=ne[j];
end;
exit(0);
end;
begin
readln(n,m);
readln(k);
for i:=1 to k do begin readln(x,y);a[x,y]:=true; end;
fillchar(fir,sizeof(fir),255);
for i:=1 to n do for j:=1 to m do if (not a[i,j])and(not usey[v(i,j)]) then add(i,j);
while true do
begin
bfs;//建分层图
if dis[n*m+1]=-1 then break;//如果到不了汇点则跳出循环
ans:=ans+dfs(0,maxlongint);
end;
writeln(ans);
end.
最后我发现我似乎是这道题唯一0ms过的人233333......
测试点#cover0.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover1.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover2.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover3.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover4.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover5.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover6.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover7.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover8.in 结果: 内存使用量: 256kB 时间使用量: 0ms
测试点#cover9.in 结果: 内存使用量: 256kB 时间使用量: 0ms