洛谷 P1034 [NOIP2002 T4] 矩形覆盖

本文介绍了一种使用深度优先搜索(DFS)结合剪枝策略解决多个矩形覆盖平面上指定点集的问题,并确保覆盖总面积最小。通过合理剪枝减少搜索空间,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

在平面上有 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)

输出格式:

输出至屏幕。格式为:

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

输入输出样例

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











~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dfs+剪枝~

对于每个点,枚举新建长方形和加入旧的长方形中两种情况,然后加上剪枝就能过,我的都是1ms左右~

剪枝有:

1.例行剪枝:now>=tot;

2.长方形数大于k;

3.剩余点数小于剩余须加的长方形数;

4.剩余点数等于剩余须加的长方形数,用已有的now值直接更新ans值,因为单个点面积为1;

5.题目中要求的剪枝:不能重合;

另外,第一次把kkz之类的变量设成全局变量,然后就WA40,玄学问题8号……


#include<cstdio>

int n,k,ans,now; 

struct node{
	int x,y;
}a[51];

struct node1{
	int x1,y1,x2,y2;
}c[5];

void add(node1 &u,node v)
{
	if(u.x1>v.x) u.x1=v.x;
	else if(u.x2<v.x) u.x2=v.x;
	if(u.y1>v.y) u.y1=v.y;
	else if(u.y2<v.y) u.y2=v.y;
}

bool pd(int u,int v)
{
	for(int i=1;i<=v;i++)
	  if(i!=u)
	  {
	  	if(c[u].x1>=c[i].x1 && c[u].x1<=c[i].x2 && c[u].y2>=c[i].y1 && c[u].y2<=c[i].y2) return 0;
	  	if(c[u].x1>=c[i].x1 && c[u].x1<=c[i].x2 && c[u].y1>=c[i].y1 && c[u].y1<=c[i].y2) return 0;
	  	if(c[u].x2>=c[i].x1 && c[u].x2<=c[i].x2 && c[u].y2>=c[i].y1 && c[u].y2<=c[i].y2) return 0;
	  	if(c[u].x2>=c[i].x1 && c[u].x2<=c[i].x2 && c[u].y1>=c[i].y1 && c[u].y1<=c[i].y2) return 0;
	  }
	return 1;
}

void dfs(int u,int v)
{
	if(now>=ans || v>k || (n-u)<(k-v)) return;
	if(u==n)
	{
		if(v==k) ans=now;return;
	}
	if(n-u==k-v)
	{
		ans=now;return;
	}
	u++;
	for(int i=1;i<=v;i++)
	{
		int kkz=now,kkx1=c[i].x1,kky1=c[i].y1,kkx2=c[i].x2,kky2=c[i].y2;
		now-=(c[i].x2-c[i].x1)*(c[i].y2-c[i].y1);add(c[i],a[u]);
		now+=(c[i].x2-c[i].x1)*(c[i].y2-c[i].y1);
		if(pd(i,v)) dfs(u,v);
		now=kkz;c[i].x1=kkx1,c[i].y1=kky1;c[i].x2=kkx2,c[i].y2=kky2;
	}
	if(v<k)
	{
		c[++v].x1=c[v].x2=a[u].x;
		c[v].y1=c[v].y2=a[u].y;
		dfs(u,v);
	}
}

int main()
{
	scanf("%d%d",&n,&k);ans=999999999;
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
	c[1].x1=c[1].x2=a[1].x;
	c[1].y1=c[1].y2=a[1].y;
	dfs(1,1);
	printf("%d\n",ans);
	return 0;
}



### NOIP 2002 矩形覆盖问题的 Python 题解 #### 问题描述 给定平面上若干个,需要用尽可能少的 k×k 的正方形来完全覆盖这些。每个正方形可以任意放置,但必须平行坐标轴。 --- #### 思路分析 该问题是典型的贪心算法应用案例。通过逐步选择最优的位置放置正方形,使得每次都能覆盖最多的未被覆盖。以下是具体实现思路: 1. **数据预处理** 将所有的按照横坐标排序,便于后续操作[^2]。 2. **贪心策略** 每次选取当前最左侧未被覆盖作为起,尝试找到一个能覆盖最多的正方形区域,并标记区域内所有为已覆盖状态。 3. **复杂度优化** 使用集合或布尔数组记录哪些已被覆盖,从而减少重复计算的时间开销。 --- #### Python 实现代码 以下是一个基于上述思路的 Python 实现方案: ```python def min_squares(points, k): # 对按 x 坐标排序 points.sort(key=lambda p: (p[0], p[1])) covered = set() count = 0 while len(covered) < len(points): max_covered_count = 0 best_square_top_left = None # 寻找最佳左上角顶 for point in points: if point not in covered: top_left_x = point[0] top_left_y = point[1] current_covered_points = [] # 枚举当前正方形内的所有可能 for candidate_point in points: if ( candidate_point[0] >= top_left_x and candidate_point[0] < top_left_x + k and candidate_point[1] >= top_left_y and candidate_point[1] < top_left_y + k ): current_covered_points.append(candidate_point) if len(current_covered_points) > max_covered_count: max_covered_count = len(current_covered_points) best_square_top_left = (top_left_x, top_left_y) # 更新已覆盖集 for point in points: if ( point[0] >= best_square_top_left[0] and point[0] < best_square_top_left[0] + k and point[1] >= best_square_top_left[1] and point[1] < best_square_top_left[1] + k ): covered.add(point) count += 1 return count # 测试用例 if __name__ == "__main__": points = [(1, 1), (2, 2), (3, 3), (6, 6), (7, 7)] k = 3 result = min_squares(points, k) print(result) # 输出最少需要多少个正方形 ``` --- #### 关键解析 1. **排序的作用** 排序是为了方便从左到右依次考虑每一个,确保每次都优先处理尚未被覆盖的最左。 2. **贪心的选择依据** 每次都试图最大化单个正方形所能覆盖的数量,这是局部最优解的一部分。 3. **时间复杂度** 外层循环次数取决于最终使用的正方形数量,而内层循环则遍历所有以评估某个候选正方形的效果。因此整体复杂度接近 O(),其中 n 是的数量。 --- #### 注意事项 - 如果输入规模较大,则需进一步优化算法性能,例如采用更高效的区间查询结构(如线段树)替代双重嵌套循环。 - 当前实现假设所有均为整数坐标;如果涉及浮数或其他特殊情况,应调整相应逻辑。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值