Avatar
“”

一个兴趣使然的追梦人

▼

#题解——洛谷 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)),用于存储所有比赛。

posted @ 2026-10-18 23:47  hermanO  阅读(15)  评论(0)    收藏  举报