POJ 2836:Rectangular Covering(状态压缩DP)
题目大意:在一个平面内有若干个点,要求用一些矩形覆盖它们,一个矩形至少覆盖两个点,可以相互重叠,求矩形最小总面积。
分析:
数据很小,很容易想到状压DP,我们把点是否被覆盖用0,1表示然后放在一起得到一个最多15位的二进制数字作为状态,对于每种状态枚举矩形,进行覆盖。
要进行一个预处理,将每种矩形多覆盖的点状态保存下来,并计算面积,然后就是DP了。
状态转移方程f[s2]=min(f[s2],f[s1]+area) s1为原来状态,s2表示后来放置矩形后的状态。
因为可以重复覆盖,计算s2不是s2=s1+a[i](a[i]为i矩形的覆盖状态),而是s2=s1 or a[i],因为用或运算,我们不能采取常用的枚举覆盖后状态,推出覆盖前状态这样进行DP,因为一个该矩形i覆盖后的状态可能对应多个原始状态(因为or),所以要枚举初始状态,再计算后来状态求解。
最后要注意当两个点在同一条平行于坐标轴的直线上时,它们对应的矩形长或宽为1.
代码:
program Rec; var f:array[0..32768]of longint; x,y:array[0..15]of longint; a,s:array[0..225]of longint; n,i,m,num,j,k:longint; function max(x,y:longint):longint; begin if x>y then max:=x else max:=y; end; function min(x,y:longint):longint; begin if x<y then min:=x else min:=y; end; procedure workfirst; var i,j:longint; begin m:=0; fillchar(a,sizeof(a),0); fillchar(s,sizeof(s),0); num:=1 shl n-1; for i:=0 to num do f[i]:=maxlongint; f[0]:=0; end; procedure workread; var i,j,k,maxx,minx,maxy,miny:longint; begin for i:=1 to n do readln(x[i],y[i]); for i:=2 to n do for j:=1 to i-1 do begin maxx:=max(x[i],x[j]); maxy:=max(y[i],y[j]); minx:=min(x[i],x[j]); miny:=min(y[i],y[j]); inc(m); for k:=1 to n do if (x[k]>=minx)and(x[k]<=maxx)and(y[k]>=miny)and(y[k]<=maxy) then inc(a[m],1 shl (k-1)); s[m]:=max(1,(maxx-minx))*max(1,(maxy-miny)); end; end; procedure workdp; var i,j,k:longint; begin for i:=0 to num do if f[i]<>maxlongint then for j:=1 to m do if i<>a[j] then f[i or a[j]]:=min(f[i or a[j]],f[i]+s[j]); end; begin assign(input,'Rec.in'); reset(input); assign(output,'Rec.out'); rewrite(output); readln(n); while n<>0 do begin workfirst; workread; workdp; writeln(f[num]); readln(n); end; close(input); close(output); end.