NOIP2002 矩形覆盖

题四 矩形覆盖(存盘名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。各个矩形必须完全分开(边线与顶点也都不能重合)。

[输入]:

  键盘输人文件名。文件格式为

   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

 

【思路】

  回溯法。

  搜索依次确定每个点属于哪一个矩形,时间复杂度为O(50^4)。最优解剪枝+如果相交则剪枝。

 

  在网上看到了DP的做法:

   f[i][j][k]=min{ f[i][l][k-1]+S(l+1,j) }

 算法并不完美(只出现了矩形包含的点连续的情况,还有可能不连续),因为数据比较弱所以才能AC,但不失为一种可以借鉴的思路。

 

【dfs代码】

 1 #include<iostream>
 2 #include<cstring>
 3 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 4 using namespace std; 
 5 
 6 const int maxn = 50+10;
 7 struct Matrix{
 8     int a,b,c,d;
 9     bool flag;
10     Matrix() { flag=false; }
11 }re[5];
12 
13 int x[maxn],y[maxn];
14 int n,m,ans;
15 
16 bool in(int x,int y,Matrix A) {
17     return (x>=A.a && x<=A.b && y>=A.c && y<=A.d);
18 }
19 
20 bool can(int i,int j) {
21     bool ans=0;
22     if(in(re[i].a,re[i].c,re[j])) return 1;
23     if(in(re[i].a,re[i].d,re[j])) return 1;
24     if(in(re[i].b,re[i].c,re[j])) return 1; 
25     if(in(re[i].b,re[i].d,re[j])) return 1;
26     return 0;
27 }
28 
29 void dfs(int d) {
30     Matrix tmp;
31     int sum=0;
32     FOR(i,1,m)
33       if(re[i].flag) 
34       {
35           sum += (re[i].b-re[i].a)*(re[i].d-re[i].c);
36           FOR(j,i+1,m)
37              if(re[j].flag && (can(i,j))) return ;
38       }
39     
40     if(sum>=ans) return ;
41     if(d>n) {  ans=sum; return ; }
42     FOR(i,1,m) {
43         tmp=re[i];
44         if(!re[i].flag) {
45             re[i].flag=1;
46             re[i].a=re[i].b=x[d];
47             re[i].c=re[i].d=y[d];
48         }
49         else {
50             re[i].a=min(re[i].a,x[d]);
51             re[i].b=max(re[i].b,x[d]);
52             re[i].c=min(re[i].c,y[d]);
53             re[i].d=max(re[i].d,y[d]);
54         }
55         dfs(d+1);
56         re[i]=tmp;
57     }
58 }
59 
60 int main() {
61     ios::sync_with_stdio(false);
62     cin>>n>>m;
63     FOR(i,1,n)  cin>>x[i]>>y[i];
64     ans=1e9;
65     dfs(1);
66     cout<<ans;
67     return 0;
68 }

 

 

【dp代码】

 1 #include<iostream>
 2 #define Max 1000000
 3 using namespace std;
 4 
 5 int n,m,ans=Max,x[52],y[52],f[52][52][5]={0};
 6 
 7 int High(int i,int j){
 8     int maxh=0,minh=1000,temp=i;
 9     while(temp<=j)
10     maxh=max(maxh,y[temp++]);
11     temp=i;
12     while(temp<=j)
13     minh=min(minh,y[temp++]);
14     return maxh-minh;
15 }
16 
17 
18 void Dp(){
19      for(int i=1;i<=n;++i)
20      for(int j=1;j<=n;++j)
21      for(int k=2;k<=m;++k)
22      f[i][j][k]=Max;
23      
24      for(int i=1;i<=n;++i)
25      for(int j=i+1;j<=n;++j)
26      f[i][j][1]=(x[j]-x[i])*High(i,j);
27      for(int i=1;i<=n;++i)
28      for(int k=1;k<=m;++k)
29      f[i][i][k]=0;
30      
31      for(int k=2;k<=m;++k)
32      for(int i=1;i<=n;++i)
33      for(int j=i+1;j<=n;++j)
34      for(int l=i+1;l<=j;++l)
35      f[i][j][k]=min(f[i][j][k],f[i][l-1][k-1]+(x[j]-x[l])*High(l,j));
36 
37      ans=min(ans,f[1][n][m]);
38 }
39 
40 int main()
41 {
42     cin>>n>>m;
43     for(int i=1;i<=n;++i)
44     cin>>x[i]>>y[i];
45     
46     for(int i=1;i<=n;++i)
47     for(int j=i+1;j<=n;++j)
48     if(x[i]>x[j]) {swap(x[i],x[j]);swap(y[i],y[j]);}
49     else if(x[i]==x[j]&&y[i]>=y[j]) swap(y[i],y[j]);
50     
51     Dp();    
52     
53     for(int i=1;i<=n;++i)
54     swap(x[i],y[i]);
55     
56     for(int i=1;i<=n;++i)
57     for(int j=i+1;j<=n;++j)
58     if(x[i]>x[j]) {swap(x[i],x[j]);swap(y[i],y[j]);}
59     else if(x[i]==x[j]&&y[i]>=y[j]) swap(y[i],y[j]);
60     
61     Dp();    
62     
63     cout<<ans<<endl;
64     return 0;
65     
66     }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值