聪明文档网

聪明文档网

最新最全的文档下载
当前位置: 首页> 矩形覆盖题解

矩形覆盖题解

时间:2018-07-01 06:03:21    下载该word文档

第四题 矩形覆盖

矩形覆盖(存盘名NOIPG4
[问题描述]:
  在平面上有 n 个点(n <= 50),每个点用一对整数坐标表示。例如:当 n4 时,4个点的坐标分另为:p111),p222),p336),P407),见图一。

  这些点可以用 k 个矩形(1<=k<=4)全部覆盖,矩形的边平行于坐标轴。当 k=2 时,可用如图二的两个矩形 sls2 覆盖,s1s2 面积和为 4。问题是当 n 个点坐标和 k 给出后,怎样才能使得覆盖所有点的 k 个矩形的面积之和为最小呢。约定:覆盖一个点的矩形面积为 0;覆盖平行于坐标轴直线上点的矩形面积也为0。各个矩形必须完全分开(边线与顶点也都不能重合)。

[输入]:
  键盘输人文件名。文件格式为
   n k
   xl y1
   x2 y2
   ... ...
   xn yn 0<=xi,yi<=500)

[输出]:
  输出至屏幕。格式为:
  一个整数,即满足条件的最小的矩形面积之和。

[输入输出样例]
d.in :
 4 2
 1 1
 2 2
 3 6
 0 7

屏幕显示:
4

分析

【题解一】

1、本题的难度较大。如果你这样认为:即在假定已用i个矩形(面积和满足最小)覆盖所有点的基础上,穷举所有2个矩形合并成1个矩形(条件是:在所有合并方案中使合并后面积最小),从而使矩形个数减少为i-1——那就错了,可是却可以通过前4组测试数据!

正确的做法是对不同的K值分别进行计算,好在K值较小,否则...

讨论:

k=1,只要求出n个点坐标的最大、最小值,就可求得矩形的位置与面积;

k=2,有2个矩形,它们只有2种分布形式:左右式(flag=0),上下式(flag=1

对于左右式,显然要先将所有点按横坐标升序排列,可将点1~i-1放入矩形1中,将点i~n放入矩形2中,求两矩形的面积之和;如果面积和比上一个值小,记下;让i2循环到n,就可完成左右式的全部搜索;

对于上下式,先将所有点按纵坐标升序排列,依此类推。

k=3,有3个矩形,它们有6种分布形式:

要用两重循环进行搜索:设i,j为循环变量,将点1~i-1放入矩形1中,点i~j-1放入矩形2中,点j~n放入矩形3中;点必须在放入前排好序(均为升序):对于flag=0,所有点按横坐标排序;对于flag=1,所有点按纵坐标排序;对于flag=2,所有点先按横坐标排序,然后点i~n按纵坐标排序;对于flag=3,所有点先按横坐标排序,然后点1~j-1按纵坐标排序;对于flag=4,所有点先按纵坐标排序,然后点1~j-1按横坐标排序;对于flag=5,所有点先按纵坐标排序,然后点i~n按横坐标排序;

至于k=4,4个矩形有22种分布形式,实在太复杂!幸好测试数据中没有K=4的情形(似乎有意放了一马?)。据说本题全国没有一人全对!(只要求K=123

程序清单

{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}

{$M 65520,0,655360}

program NOIPG4;

const maxn=50;maxk=3;

type rect=record{定义"矩形"数据类型}

l,r,t,b:word;{矩形的左边,右边,下边,上边距坐标轴的距离}

end;

vxy=record{定义""数据类型}

x,y:word;{点的横、纵坐标}

end;

var ju:array[1..maxk]of rect;

v:array[1..maxn,0..2] of vxy;v0:vxy;

n,k,i,j,ii,jj:byte;f:text;filename:string;

Smin,temp:longint;

function intersect(jui,juj:rect):boolean;{判断两矩形是否有公共点}

var b1,b2,t1,t2,l1,l2,r1,r2:word;

begin

b1:=jui.b;b2:=juj.b;t1:=jui.t;t2:=juj.t;

l1:=jui.l;l2:=juj.l;r1:=jui.r;r2:=juj.r;

intersect:=((l2<=r1) and (l2>=l1) or (r2<=r1) and (r2>=l1) or (l2<=l1)

and (r2>=r1)) and ((t2<=b1) and (t2>=t1) or (b2<=b1) and (b2>=t1)

or (b2>=b1) and (t2<=t1));

end;

function area(ju:rect):longint;{求矩形的面积}

var temp:longint;

begin

temp:=ju.b-ju.t;area:=temp*(ju.r-ju.l);

{不能直接写成area:=(ju.b-ju.t)*(ju.r-ju.l);因为这样可能会溢出!}

end;

procedure insert(v:vxy;var ju:rect);{将点放入矩形}

begin

if v.x

if v.x>ju.r then ju.r:=v.x;

if v.y

if v.y>ju.b then ju.b:=v.y;

end;

procedure init;{初始化}

begin

write('Input filename:');readln(filename);

assign(f,filename);reset(f);readln(f,n,k);

for i:=1 to n do begin

read(f,v[i,0].x,v[i,0].y);

v[i,1].x:=v[i,0].x;v[i,1].y:=v[i,0].y;

end;

for i:=1 to n-1 do{按横坐标升序排列各点,存入v[i,0]}

for j:=i+1 to n do

if v[i,0].x>v[j,0].x then begin

v0:=v[i,0];v[i,0]:=v[j,0];v[j,0]:=v0;

end;

for i:=1 to n-1 do{按纵坐标升序排列各点,存入v[i,1]}

for j:=i+1 to n do

if v[i,1].y>v[j,1].y then begin

v0:=v[i,1];v[i,1]:=v[j,1];v[j,1]:=v0;

end;

end;

procedure solve;{核心计算}

begin

smin:=maxlongint;

case k of

1:begin{K=1的情形}

ju[1].b:=v[n,1].y;ju[1].t:=v[1,1].y;

ju[1].r:=v[n,0].x;ju[1].l:=v[1,0].x;

smin:=area(ju[1]);

end;

2:for jj:=0 to 1 do begin{K=2的情形}

{flag=0,1的情形}

ju[1].b:=v[1,jj].y;ju[1].t:=v[1,jj].y;

ju[1].r:=v[1,jj].x;ju[1].l:=v[1,jj].x;

for i:=2 to n do begin

insert(v[i-1,jj],ju[1]);{将第i-1点放入矩形1}

ju[2].b:=v[i,jj].y;ju[2].t:=v[i,jj].y;{将第in点放入矩形2}

ju[2].r:=v[i,jj].x;ju[2].l:=v[i,jj].x;

for ii:=i+1 to n do insert(v[ii,jj],ju[2]);

if not intersect(ju[1],ju[2]) then begin{如果两矩形不交叉}

temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);

if temp

end;

end;

end;

3:begin

for jj:=0 to 1 do begin {flag=0,1的情形}

ju[1].b:=v[1,jj].y;ju[1].t:=v[1,jj].y;

ju[1].r:=v[1,jj].x;ju[1].l:=v[1,jj].x;

for i:=2 to n-1 do begin

insert(v[i-1,jj],ju[1]);

ju[2].b:=v[i,jj].y;ju[2].t:=v[i,jj].y;

ju[2].r:=v[i,jj].x;ju[2].l:=v[i,jj].x;

if intersect(ju[1],ju[2]) then continue;

for j:=i+1 to n do begin

insert(v[j-1,jj],ju[2]);

ju[3].b:=v[j,jj].y;ju[3].t:=v[j,jj].y;

ju[3].r:=v[j,jj].x;ju[3].l:=v[j,jj].x;

for ii:=j+1 to n do insert(v[ii,jj],ju[3]);

if intersect(ju[2],ju[3]) then continue;

temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);

if temp

end;

end;

end;

{flag=2的情形:先竖直划分大矩形;再在右矩形中水平划分}

ju[1].b:=v[1,0].y;ju[1].t:=v[1,0].y;

ju[1].r:=v[1,0].x;ju[1].l:=v[1,0].x;

for i:=2 to n-1 do begin

for ii:=1 to n do v[ii,2]:=v[ii,0];{所有点按横坐标升序排列,存入v[i,2]}

for ii:=i to n-1 do{将点in按纵坐标升序排列,存入v[i,2]}

for jj:=ii+1 to n do

if v[ii,2].y>v[jj,2].y then begin

v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;

end;{结果:所有点先按横坐标升序排列,然后点in按纵坐标升序排列}

insert(v[i-1,2],ju[1]);{将第i-1点放入矩形1}

ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;{将第i点放入矩形2}

ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;

if intersect(ju[1],ju[2]) then continue;

for j:=i+1 to n do begin

insert(v[j-1,2],ju[2]);{将第j-1点放入矩形2}

ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;{将第jn点放入矩形3}

ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;

for ii:=j+1 to n do insert(v[ii,2],ju[3]);

if intersect(ju[2],ju[3]) then continue;

temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);

if temp

end;

end;

{flag=3的情形}

for j:=3 to n do begin

for ii:=1 to n do v[ii,2]:=v[ii,0];

for ii:=1 to j-2 do

for jj:=ii+1 to j-1 do

if v[ii,2].y>v[jj,2].y then begin

v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;

end;

ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;

ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;

for ii:=j+1 to n do insert(v[ii,2],ju[3]);

for i:=2 to j-1 do begin

ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;

ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;

for ii:=i+1 to j-1 do insert(v[ii,2],ju[2]);

ju[1].b:=v[1,2].y;ju[1].t:=v[1,2].y;

ju[1].r:=v[1,2].x;ju[1].l:=v[1,2].x;

for ii:=2 to i-1 do insert(v[ii,2],ju[1]);

if intersect(ju[1],ju[2]) or intersect(ju[2],ju[3]) or

intersect(ju[1],ju[3]) then continue;

temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);

if temp

end;

end;

{flag=4的情形}

for j:=3 to n do begin

for ii:=1 to n do v[ii,2]:=v[ii,1];

for ii:=1 to j-2 do

for jj:=ii+1 to j-1 do

if v[ii,2].x>v[jj,2].x then begin

v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;

end;

ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;

ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;

for ii:=j+1 to n do insert(v[ii,2],ju[3]);

for i:=2 to j-1 do begin

ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;

ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;

for ii:=i+1 to j-1 do insert(v[ii,2],ju[2]);

ju[1].b:=v[1,2].y;ju[1].t:=v[1,2].y;

ju[1].r:=v[1,2].x;ju[1].l:=v[1,2].x;

for ii:=2 to i-1 do insert(v[ii,2],ju[1]);

if intersect(ju[1],ju[2]) or intersect(ju[2],ju[3]) or

intersect(ju[1],ju[3]) then continue;

temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);

if temp

end;

end;

{flag=5的情形}

ju[1].b:=v[1,1].y;ju[1].t:=v[1,1].y;

ju[1].r:=v[1,1].x;ju[1].l:=v[1,1].x;

for i:=2 to n-1 do begin

for ii:=1 to n do v[ii,2]:=v[ii,1];

for ii:=i to n-1 do

for jj:=ii+1 to n do

if v[ii,2].x>v[jj,2].x then begin

v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;

end;

insert(v[i-1,2],ju[1]);

ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;

ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;

if intersect(ju[1],ju[2]) then continue;

for j:=i+1 to n do begin

insert(v[j-1,2],ju[2]);

ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;

ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;

for ii:=j+1 to n do insert(v[ii,2],ju[3]);

if intersect(ju[2],ju[3]) then continue;

temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);

if temp

end;

end;

end;

end;

end;

begin{主程序}

init;

solve;

writeln(smin);

end.

点评:压轴题 据说,本次复赛主要是前三题的竞争,可见本题能得分的人相当少,但是K=1应该说是送分的,K=2也是比较容易的。通过测试,发现在K=3的第45组测试数据中仅用到了flag=1的情形,也就是说,只要写出flag=1的程序段就OK了(没写flag=0,2,3,4,5的同学偷着乐?)。

【题解二】

具体方法是将每个点极角排序然后就是一个经典的DP了:f[i,j,k]=min(f[i,j,k],f[i,t,k-1]+s[t+1,j])

另外注意要DP两次因为可以是横着的也可以是竖着的

具体实现参见以下代码:

var
  n,m,i,t,ans,last:longint;
  x,y:array[1..51] of integer;
procedure sort(l,r:integer);
var
  i,j,mid,t:integer;
begin
  i:=l;j:=r;mid:=x[(l+r) shr 1];
  repeat
    while(x[i]
    while(x[j]>mid) do dec(j);
    if i<=j then
      begin
        t:=x[i];x[i]:=x[j];x[j]:=t;
        t:=y[i];y[i]:=y[j];y[j]:=t;
              inc(i);dec(j);
      end;
  until i>j;
  if i
  if l
end;
procedure qsort(l,r:integer);
var
  i,j,mid,t:integer;
begin
  i:=l;j:=r;mid:=y[(l+r) shr 1];
  repeat
    while(y[i]
    while(y[j]>mid) do dec(j);
    if i<=j then
      begin
        t:=x[i];x[i]:=x[j];x[j]:=t;
        t:=y[i];y[i]:=y[j];y[j]:=t;
              inc(i);dec(j);
      end;
  until i>j;
  if i
  if l
end;
function hight(l,r:integer):integer;
var
  i,smax,smin:integer;
begin
  smax:=0;smin:=maxint;
  for i:=l to r do
    begin
      if y[i]>smax then smax:=y[i];
      if y[i]
    end;
  hight:=smax-smin;
end;
function min(x,y:longint):longint;
begin
  if x
end;
procedure Dynamic;
var
  j,k,p:integer;
  s:array[1..50,1..50] of longint;
  f:array[1..50,1..50,1..4] of longint;
begin
  for i:=1 to m do
    for j:=1 to m do
      for k:=1 to n do
        f[i,j,k]:=300000;
  for i:=1 to m do
    for j:=i to m do
      begin
        s[i,j]:=(x[j]-x[i])*hight(i,j);
        f[i,j,1]:=s[i,j];
      end;
  for p:=1 to m do
    for i:=1 to m-p do
      begin
        j:=i+p;
        for k:=2 to n do
          for t:=i to j-1 do
            f[i,j,k]:=min(f[i,j,k],f[i,t,k-1]+s[t+1,j]);
      end;
  ans:=min(ans,f[1,m,n]);
end;
begin
  assign(input,'t4.in');reset(input);
  assign(output,'t4.out');rewrite(output);
  readln(m,n);
  for i:=1 to m do
    readln(x[i],y[i]);
  x[m+1]:=maxint;y[m+1]:=maxint;
  ans:=maxlongint;
  sort(1,m);
  t:=x[1];last:=1;
  for i:=2 to m+1 do
    if x[i]<>t then
      begin
        qsort(last,i-1);
        t:=x[i];
        last:=i;
      end;
  Dynamic;
  for i:=1 to m do
    begin
      t:=x[i];
      x[i]:=y[i];
      y[i]:=t;
    end;
  sort(1,m);
  t:=x[1];last:=1;
  for i:=2 to m+1 do
    if x[i]<>t then
      begin
        qsort(last,i-1);
        t:=x[i];
        last:=i;
      end;
  Dynamic;
  writeln(ans);
  close(input);close(output);
end.

【题解三】

  好吧,我承认,此题真正地震撼了我,我无语。。。

   K明明是14的取值,由于数据最大K3,然后所有的做法竟然都是分情况模拟!K=1时如何,K=2时有哪些分的情况。。。(上下分,左右分),K=3的时候6种情况。

    每次都要排序X,Y坐标,算面积。

{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}
{$M 65520,0,655360}
program NOIPG4;
const maxn=50;maxk=3;
type rect=record{定义"矩形"数据类型}
          l,r,t,b:word;{矩形的左边,右边,下边,上边距坐标轴的距离}
        end;
        vxy=record{定义""数据类型}
          x,y:word;{点的横、纵坐标}
        end;
var ju:array[1..maxk]of rect;
      v:array[1..maxn,0..2] of vxy;v0:vxy;
      n,k,i,j,ii,jj:byte;f:text;filename:string;
      Smin,temp:longint;
function intersect(jui,juj:rect):boolean;{判断两矩形是否有公共点}
    var b1,b2,t1,t2,l1,l2,r1,r2:word;
    begin
      b1:=jui.b;b2:=juj.b;t1:=jui.t;t2:=juj.t;
      l1:=jui.l;l2:=juj.l;r1:=jui.r;r2:=juj.r;
      intersect:=((l2<=r1) and (l2>=l1) or (r2<=r1) and (r2>=l1) or (l2<=l1)
           and (r2>=r1)) and ((t2<=b1) and (t2>=t1) or (b2<=b1) and (b2>=t1)
           or (b2>=b1) and (t2<=t1));
    end;
function area(ju:rect):longint;{求矩形的面积}
    var temp:longint;
    begin
      temp:=ju.b-ju.t;
      area:=temp*(ju.r-ju.l);
    end;
procedure insert(v:vxy;var ju:rect);{将点放入矩形}
    begin
      if v.x
      if v.x>ju.r then ju.r:=v.x;
      if v.y
      if v.y>ju.b then ju.b:=v.y;
    end;
procedure init;{初始化}
    begin
      write('Input filename:');readln(filename);
      assign(f,filename);reset(f);readln(f,n,k);
      for i:=1 to n do begin
        read(f,v[i,0].x,v[i,0].y);
        v[i,1].x:=v[i,0].x;v[i,1].y:=v[i,0].y;
      end;
      for i:=1 to n-1 do{按横坐标升序排列各点,存入v[i,0]}
        for j:=i+1 to n do
          if v[i,0].x>v[j,0].x then begin
            v0:=v[i,0];v[i,0]:=v[j,0];v[j,0]:=v0;
          end;
      for i:=1 to n-1 do{按纵坐标升序排列各点,存入v[i,1]}
        for j:=i+1 to n do
          if v[i,1].y>v[j,1].y then begin
            v0:=v[i,1];v[i,1]:=v[j,1];v[j,1]:=v0;
          end;
    end;
procedure solve;{核心计算}
    begin
      smin:=maxlongint;
      case k of
        1:begin{K=1的情形}
            ju[1].b:=v[n,1].y;ju[1].t:=v[1,1].y;
            ju[1].r:=v[n,0].x;ju[1].l:=v[1,0].x;
            smin:=area(ju[1]);
          end;
        2:for jj:=0 to 1 do begin{K=2的情形}
            {flag=0,1的情形}
            ju[1].b:=v[1,jj].y;ju[1].t:=v[1,jj].y;
            ju[1].r:=v[1,jj].x;ju[1].l:=v[1,jj].x;
            for i:=2 to n do begin
              insert(v[i-1,jj],ju[1]);{将第i-1点放入矩形1}
              ju[2].b:=v[i,jj].y;ju[2].t:=v[i,jj].y;{将第in点放入矩形2}
              ju[2].r:=v[i,jj].x;ju[2].l:=v[i,jj].x;
              for ii:=i+1 to n do insert(v[ii,jj],ju[2]);
              if not intersect(ju[1],ju[2]) then begin{如果两矩形不交叉}
                temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);
                if temp
              end;
            end;
          end;
        3:begin
            for jj:=0 to 1 do begin {flag=0,1的情形}
              ju[1].b:=v[1,jj].y;ju[1].t:=v[1,jj].y;
              ju[1].r:=v[1,jj].x;ju[1].l:=v[1,jj].x;
              for i:=2 to n-1 do begin
                insert(v[i-1,jj],ju[1]);
                ju[2].b:=v[i,jj].y;ju[2].t:=v[i,jj].y;
                ju[2].r:=v[i,jj].x;ju[2].l:=v[i,jj].x;
                if intersect(ju[1],ju[2]) then continue;
                for j:=i+1 to n do begin
                  insert(v[j-1,jj],ju[2]);
                  ju[3].b:=v[j,jj].y;ju[3].t:=v[j,jj].y;
                  ju[3].r:=v[j,jj].x;ju[3].l:=v[j,jj].x;
                  for ii:=j+1 to n do insert(v[ii,jj],ju[3]);
                  if intersect(ju[2],ju[3]) then continue;
                  temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);
                  if temp
                end;
              end;
            end;

          {flag=2的情形:先竖直划分大矩形;再在右矩形中水平划分}
          ju[1].b:=v[1,0].y;ju[1].t:=v[1,0].y;
          ju[1].r:=v[1,0].x;ju[1].l:=v[1,0].x;
          for i:=2 to n-1 do begin
            for ii:=1 to n do v[ii,2]:=v[ii,0];{所有点按横坐标升序排列,存入v[i,2]}
            for ii:=i to n-1 do{将点in按纵坐标升序排列,存入v[i,2]}
              for jj:=ii+1 to n do
                if v[ii,2].y>v[jj,2].y then begin
                  v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;
                end;{结果:所有点先按横坐标升序排列,然后点in按纵坐标升序排列}
            insert(v[i-1,2],ju[1]);{将第i-1点放入矩形1}
            ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;{将第i点放入矩形2}
            ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;
            if intersect(ju[1],ju[2]) then continue;
            for j:=i+1 to n do begin
              insert(v[j-1,2],ju[2]);{将第j-1点放入矩形2}
              ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;{将第jn点放入矩形3}
              ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;
              for ii:=j+1 to n do insert(v[ii,2],ju[3]);
              if intersect(ju[2],ju[3]) then continue;
              temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);
              if temp
            end;
          end;

          {flag=3的情形}
          for j:=3 to n do begin
            for ii:=1 to n do v[ii,2]:=v[ii,0];
            for ii:=1 to j-2 do
              for jj:=ii+1 to j-1 do
                if v[ii,2].y>v[jj,2].y then begin
                  v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;
                end;
            ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;
            ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;
            for ii:=j+1 to n do insert(v[ii,2],ju[3]);
            for i:=2 to j-1 do begin
              ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;
              ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;
              for ii:=i+1 to j-1 do insert(v[ii,2],ju[2]);
              ju[1].b:=v[1,2].y;ju[1].t:=v[1,2].y;
              ju[1].r:=v[1,2].x;ju[1].l:=v[1,2].x;
              for ii:=2 to i-1 do insert(v[ii,2],ju[1]);
              if intersect(ju[1],ju[2]) or intersect(ju[2],ju[3]) or
                intersect(ju[1],ju[3]) then continue;
              temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);
              if temp
            end;
          end;

          {flag=4的情形}
          for j:=3 to n do begin
            for ii:=1 to n do v[ii,2]:=v[ii,1];
            for ii:=1 to j-2 do
              for jj:=ii+1 to j-1 do
                if v[ii,2].x>v[jj,2].x then begin
                  v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;
                end;
            ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;
            ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;
            for ii:=j+1 to n do insert(v[ii,2],ju[3]);
            for i:=2 to j-1 do begin
              ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;
              ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;
              for ii:=i+1 to j-1 do insert(v[ii,2],ju[2]);
              ju[1].b:=v[1,2].y;ju[1].t:=v[1,2].y;
              ju[1].r:=v[1,2].x;ju[1].l:=v[1,2].x;
              for ii:=2 to i-1 do insert(v[ii,2],ju[1]);
              if intersect(ju[1],ju[2]) or intersect(ju[2],ju[3]) or
                intersect(ju[1],ju[3]) then continue;
              temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);
              if temp
            end;
          end;

          {flag=5的情形}
          ju[1].b:=v[1,1].y;ju[1].t:=v[1,1].y;
          ju[1].r:=v[1,1].x;ju[1].l:=v[1,1].x;
          for i:=2 to n-1 do begin
            for ii:=1 to n do v[ii,2]:=v[ii,1];
            for ii:=i to n-1 do
              for jj:=ii+1 to n do
                if v[ii,2].x>v[jj,2].x then begin
                  v0:=v[ii,2];v[ii,2]:=v[jj,2];v[jj,2]:=v0;
                end;
            insert(v[i-1,2],ju[1]);
            ju[2].b:=v[i,2].y;ju[2].t:=v[i,2].y;
            ju[2].r:=v[i,2].x;ju[2].l:=v[i,2].x;
            if intersect(ju[1],ju[2]) then continue;
            for j:=i+1 to n do begin
              insert(v[j-1,2],ju[2]);
              ju[3].b:=v[j,2].y;ju[3].t:=v[j,2].y;
              ju[3].r:=v[j,2].x;ju[3].l:=v[j,2].x;
              for ii:=j+1 to n do insert(v[ii,2],ju[3]);
              if intersect(ju[2],ju[3]) then continue;
              temp:=0;for ii:=1 to k do temp:=temp+area(ju[ii]);
              if temp
            end;
          end;
        end;
      end;
    end;
BEGIN{主程序}
    init;
    solve;
    writeln(smin);
END.

【题解四】

各种剪枝优化。。。

//By LYLtim

uses math;

type rnode=record xl,xr,yu,yd:integer; end;

var n,k:byte;
    ans:longword;
    v:array[1..50]of record x,y:integer; end;
    rect:array[1..4]of rnode;

procedure init;
var i:byte;
begin
    readln(n,k);
    for i:=1 to n do with v[i] do readln(x,y);
    for i:=1 to k do with rect[i] do
        begin xl:=501;xr:=-1;yu:=-1;yd:=501; end;
    ans:=1000000;
end;{init}

procedure search(dep:byte);
var i,j:byte;
    s:longword;
    tmp:rnode;
begin
    s:=0;
    for i:=1 to k do
        if rect[i].xl<501 then
            begin
                inc(s,(rect[i].xr-rect[i].xl)*(rect[i].yu-rect[i].yd));
                if s>=ans then exit;
                for j:=i+1 to k do
                    if(rect[j].xl<501)and(rect[j].yd<=rect[i].yu)and
                      (rect[j].yu>=rect[i].yd)and(rect[j].xr>=rect[i].xl)
                      and(rect[j].xl<=rect[i].xr)then exit; //两个矩形相交就跳出
            end;
    if dep>n then begin ans:=s; exit; end;
    for i:=1 to k do
        begin
            tmp:=rect[i];
            rect[i].xl:=min(rect[i].xl,v[dep].x);
            rect[i].xr:=max(rect[i].xr,v[dep].x);
            rect[i].yu:=max(rect[i].yu,v[dep].y);
            rect[i].yd:=min(rect[i].yd,v[dep].y);
            search(dep+1);
            rect[i]:=tmp;
        end;
end;{search}

begin{main}
    init;
    search(1);
    writeln(ans);
end.

【题解五】

因为矩形最多是4个,所以简单搜索每个点属于哪个矩形即可。注意条件里写矩形间两两不相交。
var b:array[1..4,1..4]of longint;
    x,y:array[1..50]of longint;
    n,k,i,ans:longint;
procedure qsort(l,r:longint);
var i,j,midx,midy,temp:longint;
begin
    i:=l;j:=r;midx:=x[(l+r)shr 1];midy:=y[(l+r)shr 1];
    while i
      while (x[i]
      while (x[j]>midx)or((x[j]=midx)and(y[j]>midy))do dec(j);
      if i<=j then begin
        temp:=x[i];x[i]:=x[j];x[j]:=temp;
        temp:=y[i];y[i]:=y[j];y[j]:=temp;
        inc(I);dec(J);
      end;
    end;
    if i
    if l
end;
procedure init;
begin
    readln(n,k);
    for i:=1 to n do readln(x[i],y[i]);
    Ans:=maxlongint;
    qsort(1,n);
end;
function cross(i:longint):boolean;
var j:longint;
begin
    for j:=1 to k do
      if (i<>j)then
        begin
          if not((b[i,1]>b[j,2])or(b[i,3]>b[j,4])or(b[j,1]>b[i,2])or(b[j,3]>b[i,4]))then exit(true);
        end;
    exit(false);
end;
procedure dfs(t:longint);
var i,j,tmp:longint;
      a:array[1..4,1..4]of longint;
begin
    for i:=1 to k do
      if (b[i,1]<=x[t])and(x[t]<=b[i,2])and(b[i,3]<=y[t])and(y[t]<=b[i,4])then
        begin
          if t=n then
            begin
              tmp:=0;
              for j:=1 to k do
                tmp:=tmp+(b[j,2]-b[j,1])*(b[j,4]-b[j,3]);
              if tmp
            end else dfs(t+1);
          exit;
        end;
    a:=b;
    for i:=1 to k do
      begin
        b:=a;
        if (b[i,1]=-1)then
          begin
            b[i,1]:=x[t];b[i,2]:=x[t];b[i,3]:=y[t];b[i,4]:=y[t];
          end else
          begin
            if x[t]
            if x[t]>b[i,2] then b[i,2]:=x[t];
            if y[t]
            if y[t]>b[i,4] then b[i,4]:=y[t];
          end;
        if cross(I) then continue;
        tmp:=0;
        for j:=1 to k do
          tmp:=tmp+(b[j,2]-b[j,1])*(b[j,4]-b[j,3]);
        if tmp>ans then continue;
        if t=n then ans:=tmp else dfs(t+1);
      end;
end;
begin
init;
fillchar(b,sizeof(b),255);
dfs(1);
writeln(ans);
end.

【问题分析】

由于k的取值只有四种情况,所以我采用对k分类讨论的办法。

k=1时,很简单,不说了。

k=2时,将所有点按横坐标排序,用一条平行与y轴的直线将所有点分为两部分,分别用一个矩形覆盖,可得到一个解。枚举所有这样的划分方案进行求解(即对点进行枚举,用每个点及其后一个点之间的直线划分)。再按纵坐标排序,用平行与x轴的直线划分,同样方法求解。最后得到最优解。

k=3时,类似于k=2时的情况。只是划分后,一部分用一个矩形覆盖,另一部分用两个矩形覆盖,并求解。然后颠倒过来再求解。

k=4时,简单地用直线划分不一定能求得最优解(如右图,其最优解是零)。我的算法是枚举每个点,将它以及它左下方(包括正左方和正下方)的点用一个矩形覆盖,而其余的点用三个矩形覆盖。这个算法有一个缺陷,即有可能违背各个矩形必须完全分开的限制条件,而得到比实际最优解更小的结果。但对于绝大部分数据是没有问题的。另外,对于绝大部分随机生成的数据,甚至直接用直线划分也可求得最优解。

【程序清单】

{$A+,B-,D+,E+,F-,G-,I-,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+}

{$M 16384,0,655360}

Program NOIPG4;

Type

SetType=[1..50,1..2] Of LongInt;

Var

Infile:Text;

InName:String;

N,K:LongInt;

Data:SetType;{存储所有的点}

i:LongInt;

Function Solve1(Var CurSet:SetType;A,B:Integer):LongInt;

{用一个矩形覆盖CurSet中的第A个点到第B个点}

Var

i:LongInt;

Up,Down,Left,Right:LongInt;

{分别为最大纵坐标、最小纵坐标、最小横坐标、最大横坐标}

Begin

If B-A+1<=1 Then

Begin

Solve1:=0;

Exit;

End;

Up:=0;

Down:=500;

Left:=500;

Right:=0;

For i:=A To B Do

Begin

If CurSet[i,1]

If CurSet[i,1]>Right Then Right:=CurSet[i,1];

If CurSet[i,2]

If CurSet[i,2]>Up Then Up:=CurSet[i,2];

End;

Solve1:=(Up-Down)*(Right-Left);

End;{Solve1}

Function Solve2(CurSet:SetType;A,B:Integer):LongInt;

{用两个矩形覆盖CurSet中的第A个点到第B个点}

Var

i,j,X,Y,Best,q:LongInt;

Begin

If B-A+1<=2 Then

Begin{只有或不到两个点}

Solve2:=0;

Exit;

End;

Best:=MaxLongInt;

For i:=A To B-1 Do{以横坐标排序(冒泡排序)}

For j:=i+1 To B Do

If CurSet[i,1]>CurSet[j,1] Then

Begin

X:=CurSet[i,1]; Y:=CurSet[i,2];

CurSet[i]:=CurSet[j];

CurSet[j,1]:=X; CurSet[j,2]:=Y;

End;

For i:=A To B-1 Do

Begin

If CurSet[i,1]=CurSet[i+1,1] Then Continue;

{i个点与第(i+1)个点横坐标相同,不能在它们之间划分}

q:=Solve1(CurSet,A,i)+Solve1(CurSet,i+1,B);

If Best>q Then Best:=q;

End;

For i:=A To B-1 Do{按纵坐标排序}

For j:=i+1 To B Do

If CurSet[i,2]>CurSet[j,2] Then

Begin

X:=CurSet[i,1]; Y:=CurSet[i,2];

CurSet[i]:=CurSet[j];

CurSet[j,1]:=X; CurSet[j,2]:=Y;

End;

For i:=A To B-1 Do

Begin

If CurSet[i,2]=CurSet[i+1,2] Then Continue;

q:=Solve1(CurSet,A,i)+Solve1(CurSet,i+1,B);

If Best>q Then Best:=q;

End;

Solve2:=Best;

End;{Solve2}

Function Solve3(CurSet:SetType;A,B:Integer):LongInt;

{用三个矩形覆盖CurSet中的第A个点到第B个点,类似于Solve2}

Var

i,j,X,Y,Best,q:LongInt;

Begin

If B-A+1<=3 Then

Begin

Solve3:=0;

Exit;

End;

Best:=MaxLongInt;

For i:=A To B-1 Do

For j:=i+1 To B Do

If CurSet[i,1]>CurSet[j,1] Then

Begin

X:=CurSet[i,1]; Y:=CurSet[i,2];

CurSet[i]:=CurSet[j];

CurSet[j,1]:=X; CurSet[j,2]:=Y;

End;

For i:=A To B-1 Do

Begin

If CurSet[i,1]=CurSet[i+1,1] Then Continue;

{直线一边用一个矩形覆盖,另一边用两个矩形覆盖}

q:=Solve1(CurSet,A,i)+Solve2(CurSet,i+1,B);

If Best>q Then Best:=q;

q:=Solve2(CurSet,A,i)+Solve1(CurSet,i+1,B);

If Best>q Then Best:=q;

End;

For i:=A To B-1 Do

For j:=i+1 To B Do

If CurSet[i,2]>CurSet[j,2] Then

Begin

X:=CurSet[i,1]; Y:=CurSet[i,2];

CurSet[i]:=CurSet[j];

CurSet[j,1]:=X; CurSet[j,2]:=Y;

End;

For i:=A To B-1 Do

Begin

If CurSet[i,2]=CurSet[i+1,2] Then Continue;

q:=Solve1(CurSet,A,i)+Solve2(CurSet,i+1,B);

If Best>q Then Best:=q;

q:=Solve2(CurSet,A,i)+Solve1(CurSet,i+1,B);

If Best>q Then Best:=q;

End;

Solve3:=Best;

End;{Solve3}

Function Solve4(CurSet:SetType;A,B:Integer):LongInt;

{用四个矩形覆盖CurSet中的第A个点到第B个点}

Var

i,j,X,Y,Best,q,P1,P2:LongInt;

Set1,Set2:SetType;

Begin

If B-A+1<=4 Then

Begin

Solve4:=0;

Exit;

End;

Best:=MaxLongInt;

For i:=A To B Do{枚举每个点}

Begin

{将第i个点及其左下方(包括正左方和正下方)的点置于Set1中,其余的点置于Set2}

X:=Data[i,1]; Y:=Data[i,2];

P1:=0; P2:=0;

For j:=A To B Do

If (Data[j,1]<=X) And (Data[j,2]<=Y) Then

Begin

Inc(P1);

Set1[P1]:=Data[j];

End

Else Begin

Inc(P2);

Set2[P2]:=Data[j];

End;

q:=Solve1(Set1,1,P1)+Solve3(Set2,1,P2);

{用一个矩形覆盖Set1中的点,用三个矩形覆盖Set2中的点}

If Best>q Then Best:=q;

End;

Solve4:=Best;

End;{Solve4}

Begin

ReadLn(InName);

Assign(InFile,InName);

Reset(InFile);

ReadLn(InFile,N,K);

For i:=1 To N Do

ReadLn(InFile,Data[i,1],Data[i,2]);

Close(InFile);

Case k Of{分类讨论}

1:

Begin

WriteLn(Solve1(Data,1,N));

End;

2:

Begin

WriteLn(Solve2(Data,1,N));

End;

3:

Begin

WriteLn(Solve3(Data,1,N));

End;

4:

Begin

WriteLn(Solve4(Data,1,N));

End;

End;

End.

免费下载 Word文档免费下载: 矩形覆盖题解

  • 29.8

    ¥45 每天只需1.0元
    1个月 推荐
  • 9.9

    ¥15
    1天
  • 59.8

    ¥90
    3个月

选择支付方式

  • 微信付款
郑重提醒:支付后,系统自动为您完成注册

请使用微信扫码支付(元)

订单号:
支付后,系统自动为您完成注册
遇到问题请联系 在线客服

常用手机号:
用于找回密码
图片验证码:
看不清?点击更换
短信验证码:
新密码:
 
绑定后可用手机号登录
请不要关闭本页面,支付完成后请点击【支付完成】按钮
遇到问题请联系 在线客服