1 条题解
-
0
「JOI 2023 Final」宣传 2 题解
题目分析
给定 位居民,第 位居民住在位置 ,影响力为 。如果给居民 送书,那么所有满足 的居民 都会买书。求最少需要给多少居民送书,才能让所有居民都有书。
代码思路
核心转化
将条件 转化为:
- 居民 能覆盖居民 当且仅当:$$X_i - E_i \leq X_j - E_j \quad \text{且} \quad X_j + E_j \leq X_i + E_i $$
定义:
- (左边界)
- (右边界)
则居民 能覆盖居民 当且仅当区间 被区间 包含。
算法步骤
- 预处理:为每个居民计算 和
- 排序:按 从大到小排序, 相同时按 从大到小排序
- 贪心选择:
- 维护变量
mi记录当前已覆盖的最左边界 - 遍历排序后的居民,如果当前居民的 ,则选择该居民,并更新
mi = L_i
- 维护变量
代码解析
#include <bits/stdc++.h> using namespace std; long long n, ans, x, y, mi = -1000000000000000000; struct s { long long l, r; } a[1000000]; // 排序规则:按r从大到小,r相同时按l从大到小 bool cmp(s a, s b) { if (a.r == b.r) return a.l > b.l; else return a.r > b.r; } int main() { cin >> n; for (int i = 1; i <= n; i++) { cin >> x >> y; a[i].l = y - x; // L = X - E a[i].r = y + x; // R = X + E } sort(a + 1, a + n + 1, cmp); for (int i = 1; i <= n; i++) if (a[i].l > mi) { // 如果当前居民左边界大于已覆盖范围 ans++; // 选择该居民 mi = a[i].l; // 更新已覆盖的最左边界 } cout << ans; return 0; }算法原理
排序策略的意义
- 按 从大到小排序:优先处理覆盖范围大的居民
- 相同时按 从大到小排序:在覆盖范围相同的情况下,优先处理左边界更靠右的居民
贪心选择的逻辑
条件
if (a[i].l > mi)的含义:- 如果当前居民的左边界 大于已覆盖的最左边界
mi,说明这个居民无法被已选择的居民覆盖 - 因此需要选择这个居民来扩展覆盖范围
更新
mi = a[i].l的含义:- 记录已选择居民中的最小左边界
- 后续居民如果左边界小于等于这个值,说明可以被已选择的居民覆盖
正确性说明
这个解法基于以下观察:
- 如果居民 能覆盖居民 ,那么 且
- 按 降序排序后,如果当前居民的 大于之前所有选择居民的 ,说明它不能被任何已选择的居民覆盖
- 因此必须选择这个居民来确保覆盖
复杂度分析
- 时间复杂度:,主要来自排序
- 空间复杂度:,存储居民数据
该解法通过巧妙的排序和贪心策略,将复杂的最优化问题转化为简单的线性扫描,实现了高效求解。
- 1
信息
- ID
- 5603
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者