POJ2836 Rectangular Covering(状压DP)
题目是平面上n个点,要用若干个矩形盖住它们,每个矩形上至少要包含2个点,问要用的矩形的面积和最少是多少。
容易反证得出每个矩形上四个角必定至少覆盖了两个点。然后就状压DP:
- dp[S]表示覆盖的点集为S要用的最少矩形面积
转移我一开始是未覆盖的点和已覆盖的点构成一个矩形来转移,这是错的,因为这样子的结果是所有矩形都相连的最小面积和。
正确的是枚举还未被覆盖的矩形来转移。转移我用我为人人+队列。
有一点要注意的是,覆盖(0,0)和(0,2)两点要用的矩形是1*2,所以要特判一下两点斜率0或不存在时构成的矩形。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 #define INF (1<<29) 7 8 bool isIn(int x1,int y1,int x2,int y2,int x,int y){ 9 if(x1>x2) swap(x1,x2); 10 if(y1>y2) swap(y1,y2); 11 return x1<=x && x<=x2 && y1<=y && y<=y2; 12 } 13 int area(int x1,int y1,int x2,int y2){ 14 if(x1==x2) ++x2; 15 if(y1==y2) ++y2; 16 if(x1>x2) swap(x1,x2); 17 if(y1>y2) swap(y1,y2); 18 return (x2-x1)*(y2-y1); 19 } 20 int d[1<<15],sta[15][15]; 21 bool inque[1<<15]; 22 int main(){ 23 int n,x[15],y[15]; 24 while(~scanf("%d",&n) && n){ 25 for(int i=0; i<n; ++i) scanf("%d%d",x+i,y+i); 26 for(int i=0; i<(1<<n); ++i) d[i]=INF; 27 queue<int> que; 28 memset(inque,0,sizeof(inque)); 29 for(int i=0; i<n; ++i){ 30 for(int j=i+1; j<n; ++j){ 31 int s=0; 32 for(int k=0; k<n; ++k){ 33 if(isIn(x[i],y[i],x[j],y[j],x[k],y[k])) s|=(1<<k); 34 } 35 sta[i][j]=sta[j][i]=s; 36 if(d[s]>area(x[i],y[i],x[j],y[j])){ 37 d[s]=area(x[i],y[i],x[j],y[j]); 38 if(!inque[s|sta[i][j]]){ 39 inque[s]=1; 40 que.push(s); 41 } 42 } 43 } 44 } 45 while(!que.empty()){ 46 int s=que.front(); que.pop(); 47 for(int i=0; i<n; ++i){ 48 for(int j=i+1; j<n; ++j){ 49 if(((s>>i)&1) && ((s>>j)&1)) continue; 50 if(d[s|sta[i][j]]>d[s]+area(x[i],y[i],x[j],y[j])){ 51 d[s|sta[i][j]]=d[s]+area(x[i],y[i],x[j],y[j]); 52 if(!inque[s|sta[i][j]]){ 53 inque[s|sta[i][j]]=1; 54 que.push(s|sta[i][j]); 55 } 56 } 57 } 58 } 59 inque[s]=0; 60 } 61 printf("%d\n",d[(1<<n)-1]); 62 } 63 return 0; 64 }