题目描述
在平面上有 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;
}