矩形覆盖

题目描述

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

输入输出格式

输入格式:
n k xl y1 x2 y2 … …

xn yn (0<=xi,yi<=500)

输出格式:
输出至屏幕。格式为:

一个整数,即满足条件的最小的矩形面积之和。

输入输出样例

输入样例#1:
4 2
1 1
2 2
3 6
0 7
输出样例#1:
4

分析:这是一道典型的搜索题(毕竟在luogu的分类也是搜索),k的范围不大,所以我们可以以k为基准,讨论每一个点属于那个矩形,在搜索的同时判断这种方法是否合法,不合法就直接进行剪枝。我在一开始码这道题目的时候,思路是把n个点随机分成k份,再判断这种方式合不合法,这样会导致时间效率过低。在参考了某位大神的代码之后,对自己的代码进行了改进,认为还是本题很好的解决方法的,参考如下(有详细的注释O(∩_∩)O~):

这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

int n,m,ans=10000000;
struct node{
    int x,y;
};
node a[510];
struct node2{
    int l,r,u,d;
    bool f;
};
node2 p[5];

bool zai(node2 a,int x,int y)
{
    if (x<=a.r&&x>=a.l&&y<=a.u&&y>=a.d)
      return 1;
    return 0;
}

bool pd(node2 a,node2 b)  //判断是否包含 
{
    if (zai(a,b.l,b.u)) return 1;  //左上角   任何一个顶点在已知矩阵内就不合法 
    if (zai(a,b.l,b.d)) return 1;  //左下角 
    if (zai(a,b.r,b.u)) return 1;  //右上角 
    if (zai(a,b.r,b.d)) return 1;  //右下角 
    return 0;
}

int ss(int t)
{
    int i,j,v=0;
    for (i=1;i<=m;i++)  //在进行选点之前,先把当前状态判断一下 
    {
        if (p[i].f)  //征用了这块地 
        {
            for (j=1;j<=m;j++)  
            {
                if (i!=j&&p[j].f&&pd(p[i],p[j]))  //j也被征用,同时j在i内 
                   return 0;  //这种方案不合法,直接回溯 
            }
        }
        v+=(p[i].r-p[i].l)*(p[i].u-p[i].d);  //合法时加入面积 
    }
    if (v>=ans)  //当前的面积已经比ans大了,不合法,回溯 
       return 0;
    if (t>n)  //所有点都加入了,而且通过了上面的考验 
    {
        ans=v;
        return 0;
    }
    for (i=1;i<=m;i++)  //真正的搜索开始啦,循环我们要把这个点加入哪块矩阵 
    {
        node2 tmp=p[i];
        if (p[i].f==0)  //崭新的矩阵 
        {
            p[i].f=1;
            p[i].l=p[i].r=a[t].x;
            p[i].u=p[i].d=a[t].y;
            ss(t+1);
            p[i]=tmp;  //回溯要彻底 
        } 
        else
        {
            p[i].l=min(p[i].l,a[t].x);  //维护边界值 
            p[i].r=max(p[i].r,a[t].x);
            p[i].u=max(p[i].u,a[t].y);
            p[i].d=min(p[i].d,a[t].y);
            ss(t+1);
            p[i]=tmp;
        }
    }
}

int main()
{
    int i=1;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
      scanf("%d%d",&a[i].x,&a[i].y);
    ss(1);
    printf("%d",ans);
    return 0;
}

转载于:https://www.cnblogs.com/wutongtong3117/p/7673660.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值