#题解——洛谷 P1803 凌乱的 yyy / 线段覆盖(贪心)
一、题目分析
给定 (n) 场比赛,每场比赛有开始时间和结束时间。参加一场比赛必须完整参加,并且不能与其他比赛的时间冲突。
求最多可以参加多少场比赛。
本题的关键在于:如何选择比赛,才能让参加的比赛数量尽可能多?
如果按照开始时间排序,可能会优先选择一场持续时间很长的比赛,从而错过后面多场比赛。因此,单纯按照开始时间排序并不能保证最优解。
二、贪心思路
我们采用贪心策略:
优先选择结束时间最早且与已选比赛不冲突的比赛。
原因是结束时间越早,就越能为后续比赛留出时间,从而增加参加更多比赛的机会。
具体步骤如下:
读入所有比赛的开始时间和结束时间。
按照结束时间从小到大排序。
维护变量 last,表示上一场已参加比赛的结束时间。
遍历所有比赛,如果当前比赛的开始时间大于等于 last,说明当前比赛可以参加。
参加当前比赛后,令 cnt++,并更新 last 为当前比赛的结束时间。
遍历结束后,输出 cnt。
需要注意:题目允许一场比赛结束后,另一场比赛立即开始,因此判断条件应为 k[i].x >= last。
三、AC 代码
#include <bits/stdc++.h>
using namespace std;
struct Node {
int x;
int y;
};
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<Node> k(n);
for (int i = 0; i < n; i++) {
cin >> k[i].x >> k[i].y;
}
sort(k.begin(), k.end(), [](const Node& M, const Node& N) {
return M.y < N.y;
});
int last = -1;
int cnt = 0;
for (int i = 0; i < n; i++) {
if (k[i].x >= last) {
cnt++;
last = k[i].y;
}
}
cout << cnt;
return 0;
}
四、正确性证明
下面说明为什么按照结束时间排序,并每次选择符合条件的最早结束比赛,可以得到最优解。
设按照结束时间排序后,第一场比赛是所有比赛中结束时间最早的比赛。
考虑任意一个最优方案。如果该方案选择的第一场比赛不是这场最早结束的比赛,那么我们可以将方案中的第一场比赛替换为结束时间更早的比赛。
由于替换后的比赛结束时间不晚于原来的比赛,因此原方案中后续参加的比赛仍然可以参加,方案的比赛数量不会减少。
所以,存在一个最优方案包含贪心算法选择的第一场比赛。
在选择这场比赛后,剩余问题就变成:在开始时间不早于当前比赛结束时间的所有比赛中,最多还能参加多少场。
对剩余问题重复上述论证,可以得到贪心算法每一步的选择都不会损失最优性。因此,算法最终得到的比赛数量最大。
五、复杂度分析
时间复杂度:(O(n\log n))。排序需要 (O(n\log n)),遍历需要 (O(n))。
空间复杂度:(O(n)),用于存储所有比赛。

浙公网安备 33010602011771号