CodeVS 1022-覆盖

原题

题目描述 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 
原文地址:https://www.cnblogs.com/HAdolf-HQY/p/6575030.html