矩形覆盖题解讲解.docx
《矩形覆盖题解讲解.docx》由会员分享,可在线阅读,更多相关《矩形覆盖题解讲解.docx(32页珍藏版)》请在冰豆网上搜索。
矩形覆盖题解讲解
第四题 矩形覆盖
矩形覆盖(存盘名NOIPG4)
[问题描述]:
在平面上有n个点(n<=50),每个点用一对整数坐标表示。
例如:
当n=4时,4个点的坐标分另为:
p1(1,1),p2(2,2),p3(3,6),P4(0,7),见图一。
这些点可以用k个矩形(1<=k<=4)全部覆盖,矩形的边平行于坐标轴。
当k=2时,可用如图二的两个矩形sl,s2覆盖,s1,s2面积和为4。
问题是当n个点坐标和k给出后,怎样才能使得覆盖所有点的k个矩形的面积之和为最小呢。
约定:
覆盖一个点的矩形面积为0;覆盖平行于坐标轴直线上点的矩形面积也为0。
各个矩形必须完全分开(边线与顶点也都不能重合)。
[输入]:
键盘输人文件名。
文件格式为
nk
xly1
x2y2
......
xnyn(0<=xi,yi<=500)
[输出]:
输出至屏幕。
格式为:
一个整数,即满足条件的最小的矩形面积之和。
[输入输出样例]
d.in:
42
11
22
36
07
屏幕显示:
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中,求两矩形的面积之和;如果面积和比上一个值小,记下;让i从2循环到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=1,2,3)
程序清单
{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}
{$M65520,0,655360}
programNOIPG4;
constmaxn=50;maxk=3;
typerect=record{定义"矩形"数据类型}
l,r,t,b:
word;{矩形的左边,右边,下边,上边距坐标轴的距离}
end;
vxy=record{定义"点"数据类型}
x,y:
word;{点的横、纵坐标}
end;
varju:
array[1..maxk]ofrect;
v:
array[1..maxn,0..2]ofvxy;v0:
vxy;
n,k,i,j,ii,jj:
byte;f:
text;filename:
string;
Smin,temp:
longint;
functionintersect(jui,juj:
rect):
boolean;{判断两矩形是否有公共点}
varb1,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;
functionarea(ju:
rect):
longint;{求矩形的面积}
vartemp:
longint;
begin
temp:
=ju.b-ju.t;area:
=temp*(ju.r-ju.l);
{不能直接写成area:
=(ju.b-ju.t)*(ju.r-ju.l);因为这样可能会溢出!
}
end;
procedureinsert(v:
vxy;varju:
rect);{将点放入矩形}
begin
ifv.x=v.x;
ifv.x>ju.rthenju.r:
=v.x;
ifv.y=v.y;
ifv.y>ju.bthenju.b:
=v.y;
end;
procedureinit;{初始化}
begin
write('Inputfilename:
');readln(filename);
assign(f,filename);reset(f);readln(f,n,k);
fori:
=1tondobegin
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;
fori:
=1ton-1do{按横坐标升序排列各点,存入v[i,0]}
forj:
=i+1tondo
ifv[i,0].x>v[j,0].xthenbegin
v0:
=v[i,0];v[i,0]:
=v[j,0];v[j,0]:
=v0;
end;
fori:
=1ton-1do{按纵坐标升序排列各点,存入v[i,1]}
forj:
=i+1tondo
ifv[i,1].y>v[j,1].ythenbegin
v0:
=v[i,1];v[i,1]:
=v[j,1];v[j,1]:
=v0;
end;
end;
proceduresolve;{核心计算}
begin
smin:
=maxlongint;
casekof
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:
forjj:
=0to1dobegin{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;
fori:
=2tondobegin
insert(v[i-1,jj],ju[1]);{将第i-1点放入矩形1}
ju[2].b:
=v[i,jj].y;ju[2].t:
=v[i,jj].y;{将第i至n点放入矩形2}
ju[2].r:
=v[i,jj].x;ju[2].l:
=v[i,jj].x;
forii:
=i+1tondoinsert(v[ii,jj],ju[2]);
ifnotintersect(ju[1],ju[2])thenbegin{如果两矩形不交叉}
temp:
=0;forii:
=1tokdotemp:
=temp+area(ju[ii]);
iftemp=temp;
end;
end;
end;
3:
begin
forjj:
=0to1dobegin{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;
fori:
=2ton-1dobegin
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;
ifintersect(ju[1],ju[2])thencontinue;
forj:
=i+1tondobegin
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;
forii:
=j+1tondoinsert(v[ii,jj],ju[3]);
ifintersect(ju[2],ju[3])thencontinue;
temp:
=0;forii:
=1tokdotemp:
=temp+area(ju[ii]);
iftemp=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;
fori:
=2ton-1dobegin
forii:
=1tondov[ii,2]:
=v[ii,0];{所有点按横坐标升序排列,存入v[i,2]}
forii:
=iton-1do{将点i至n按纵坐标升序排列,存入v[i,2]}
forjj:
=ii+1tondo
ifv[ii,2].y>v[jj,2].ythenbegin
v0:
=v[ii,2];v[ii,2]:
=v[jj,2];v[jj,2]:
=v0;
end;{结果:
所有点先按横坐标升序排列,然后点i至n按纵坐标升序排列}
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;
ifintersect(ju[1],ju[2])thencontinue;
forj:
=i+1tondobegin
insert(v[j-1,2],ju[2]);{将第j-1点放入矩形2}
ju[3].b:
=v[j,2].y;ju[3].t:
=v[j,2].y;{将第j至n点放入矩形3}
ju[3].r:
=v[j,2].x;ju[3].l:
=v[j,2].x;
forii:
=j+1tondoinsert(v[ii,2],ju[3]);
ifintersect(ju[2],ju[3])thencontinue;
temp:
=0;forii:
=1tokdotemp:
=temp+area(ju[ii]);
iftemp=temp;
end;
end;
{flag=3的情形}
forj:
=3tondobegin
forii:
=1tondov[ii,2]:
=v[ii,0];
forii:
=1toj-2do
forjj:
=ii+1toj-1do
ifv[ii,2].y>v[jj,2].ythenbegin
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;
forii:
=j+1tondoinsert(v[ii,2],ju[3]);
fori:
=2toj-1dobegin
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;
forii:
=i+1toj-1doinsert(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;
forii:
=2toi-1doinsert(v[ii,2],ju[1]);
ifintersect(ju[1],ju[2])orintersect(ju[2],ju[3])or
intersect(ju[1],ju[3])thencontinue;
temp:
=0;forii:
=1tokdotemp:
=temp+area(ju[ii]);
iftemp=temp;
end;
end;
{flag=4的情形}
forj:
=3tondobegin
forii:
=1tondov[ii,2]:
=v[ii,1];
forii:
=1toj-2do
forjj:
=ii+1toj-1do
ifv[ii,2].x>v[jj,2].xthenbegin
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;
forii:
=j+1tondoinsert(v[ii,2],ju[3]);
fori:
=2toj-1dobegin
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;
forii:
=i+1toj-1doinsert(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;
forii:
=2toi-1doinsert(v[ii,2],ju[1]);
ifintersect(ju[1],ju[2])orintersect(ju[2],ju[3])or
intersect(ju[1],ju[3])thencontinue;
temp:
=0;forii:
=1tokdotemp:
=temp+area(ju[ii]);
iftemp=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;
fori:
=2ton-1dobegin
forii:
=1tondov[ii,2]:
=v[ii,1];
forii:
=iton-1do
forjj:
=ii+1tondo
ifv[ii,2].x>v[jj,2].xthenbegin
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;
ifintersect(ju[1],ju[2])thencontinue;
forj:
=i+1tondobegin
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;
forii:
=j+1tondoinsert(v[ii,2],ju[3]);
ifintersect(ju[2],ju[3])thencontinue;
temp:
=0;forii:
=1tokdotemp:
=temp+area(ju[ii]);
iftemp=temp;
end;
end;
end;
end;
end;
begin{主程序}
init;
solve;
writeln(smin);
end.
点评:
压轴题据说,本次复赛主要是前三题的竞争,可见本题能得分的人相当少,但是K=1应该说是送分的,K=2也是比较容易的。
通过测试,发现在K=3的第4、5组测试数据中仅用到了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]ofinteger;
proceduresort(l,r:
integer);
var
i,j,mid,t:
integer;
begin
i:
=l;j:
=r;mid:
=x[(l+r)shr1];
repeat
while(x[i] while(x[j]>mid)dodec(j);
ifi<=jthen
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;
untili>j;
ifi iflend;
procedureqsort(l,r:
integer);
var
i,j,mid,t:
integer;
begin
i:
=l;j:
=r;mid:
=y[(l+r)shr1];
repeat
while(y[i] while(y[j]>mid)dodec(j);
ifi<=jthen
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;
untili>j;
ifi iflend;
functionhight(l,r:
integer):
integer;
var
i,smax,smin:
integer;
begin
smax:
=0;smin:
=maxint;
fori:
=ltordo
begin
ify[i]>smaxthensmax:
=y[i];
ify[i]=y[i];
end;
hight:
=smax-smin;
end;
functionmin(x,y:
longint):
longint;
begin
ifx=xelsemin:
=y;
end;
procedureDynamic;
var
j,k,p:
integer;
s:
array[1..50,1..50]oflongint;
f:
array[1..50,1..50,1..4]oflongint;
begin
fori:
=1tomdo
forj:
=1tomdo
fork:
=1tondo
f[i,j,k]:
=300000;
fori:
=1tomdo
forj:
=itomdo
begin
s[i,j]:
=(x[j]-x[i])*hight(i,j);
f[i,j,1]:
=s[i,j];
end;
forp:
=1tomdo
fori:
=1tom-pdo
begin
j:
=i+p;
fork:
=2tondo
fort:
=itoj-1do
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);
fori:
=1tomdo
readln(x[i],y[i]);
x[m+1]:
=maxint;y[m+1]:
=maxint;
ans:
=maxlongint;
sort(1,m);
t:
=x[1];last:
=1;
fori:
=2tom+1do
ifx[i]<>tthen
begin
qsort(last,i-1);
t:
=x[i];
last:
=i;
end;
Dynamic;