1 条题解
-
0
题目解析
本题的核心是:给定平面上 个点(怪物),你可以至多移动一个点到任意空位置,然后选择一个轴对齐矩形覆盖所有点,矩形内每个格子花费 硬币,问最小花费。
关键观察
1. 不移动情况下的最小矩形
如果不移动任何点,覆盖所有点的最小矩形由坐标的极值决定:
- 宽度:
- 高度:
面积为:
$$\text{ans} = (\max x - \min x + 1) \times (\max y - \min y + 1) $$2. 移动一个点的作用
移动一个点的目的是减少矩形的一维宽度。例如:
- 如果一个点是 坐标的最大值点,移除它后新的 变成第二大的
- 类似地,如果一个点是 坐标的最小值点,移除它后新的 变成第二小的
因此,移动某个点可以消除它在某个轴向上作为极值的影响。
3. 移动后能否放入原矩形?
对于除了被移动点 之外的 个点,它们的最小矩形面积是:
其中宽度和高度由剩余的 个点的极值决定。
如果 ,意味着这个矩形正好被这 个点填满(没有空位)。此时,如果我们想把这个点移动到这个矩形内部,就必须扩大矩形的一边,因为矩形内没有空位了。
如果 ,说明矩形内有空位,我们可以直接把点移动到任意空位上,不需要扩大矩形。
算法步骤
1. 预处理所有极值
我们需要快速知道:去掉某个点后,新的极值是多少。
这可以通过维护每维的最大值和次大值、最小值和次小值来实现:
mx1:当前最大 坐标mx2:当前第二大 坐标mn1:当前最小 坐标mn2:当前第二小 坐标
对于 坐标同理。
2. 数据结构设计
代码中定义了
min_max结构体,用于维护某一维的统计信息:- 构造函数:用前两个点初始化
mx1, mx2, mn1, mn2 add(x):加入一个新点,更新四个值get_seg(x):返回忽略值为 的点后的区间长度(即 )
get_seg(x)的实现逻辑:int get_seg(int x){ pair<int, int> res = {mn1, mx1}; // 当前最小和最大 if(x == mn1) res.x = mn2; // 如果忽略的是最小值,用次小值代替 if(x == mx1) res.y = mx2; // 如果忽略的是最大值,用次大值代替 return res.y - res.x + 1; // 返回区间长度 }3. 整体算法流程
步骤 1:预处理所有点的 和 的极值信息
min_max xc(coord[0].x, coord[1].x), yc(coord[0].y, coord[1].y); for(int i = 2; i < n; ++i){ xc.add(coord[i].x); yc.add(coord[i].y); }步骤 2:计算不移动任何点时的答案
int ans = xc.get_seg(-1) * yc.get_seg(-1); // -1 表示不忽略任何点步骤 3:尝试移动每个点
对于第 个点,计算忽略它后的矩形宽度和高度:
int x = xc.get_seg(coord[i].x); int y = yc.get_seg(coord[i].y);- 如果 ,说明矩形被剩余点填满,需要扩大一边:$$\text{新面积} = \min((x+1) \times y,\ x \times (y+1)) $$
- 否则,可以直接放入矩形内:
更新答案:
ans = min(ans, 新面积);步骤 4:输出答案
样例验证
样例 1:,点
不移动时:,宽度 ;,高度 ;面积
尝试移动 :
- 剩余点:
- ,宽度 ;,高度 ;
- 可直接放入:面积
移动 或 同理。
但最优解是什么?实际答案样例输出是 。这是因为题目中你可以在移动点之前选择一个新的位置,然后选择矩形覆盖所有点。移动后总点数还是 ,但矩形可以更小。
重新理解:移动一个点后,所有 个点(包括移动后的)都被矩形覆盖。所以实际计算的是:移动后的 个点的最小矩形面积。
修正理解
算法中
x * y是忽略该点后剩余 个点的矩形面积。但我们要覆盖 个点,所以:- 如果能放进矩形内,矩形大小不变:面积
- 如果不能放进(填满了),需要扩大一边:面积
所以面积公式正确。
复杂度分析
- 时间复杂度:,每个点只处理一次
- 空间复杂度:,存储所有点坐标
由于所有测试用例的 之和不超过 ,完全可行。
总结
本题的核心技巧:
- 极值维护:用最大值/次大值、最小值/次小值快速处理"去掉一个点"后的极值变化
- 分类讨论:
- 矩形有空位 → 直接放入
- 矩形被填满 → 扩大一边
- 贪心枚举:尝试移动每个点,取最小值
这样可以在 时间内解决每个测试用例。
- 1
信息
- ID
- 7094
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者