一、题意

输入M以及若干区间[xi, yi),要求你从中取出最少的区间,使得这些区间能覆盖[0, M)。
输出最少的数量,以及选出的区间。

二、解析

经典的区间覆盖问题。
用贪心的思想。将所有区间按照左端点从小到大排列。
用cur表示当前的“截取点”,在截取点之前的长度是毫无意义的。比如,在初始状态时由于我们要覆盖[0,M),所以cur=0,此时对于一个区间比如[-2, 3),它小于0的那一段是没有用的。
因此我们首先从区间中遍历到的是会被截取点截断的若干区间,在这些区间中,我们贪心地选择剩余长度最长的(尽可能覆盖更多的长度),将它地右端点作为max_b,此时我们需要覆盖的区间就变成了[max_b,M)。于是类似的,在下一轮时我们将截取点cur:=max_b,然后以此类推。
当max_b>=M时,说明已经完全覆盖完毕了。

三、代码

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct Internal {
    int l, r;
    Internal() {}
    Internal(int l, int r) : l(l), r(r) {}
    bool operator < (const Internal & rhs) const {
        return l < rhs.l;
    }
};
int kase, M;

int main() {
    cin >> kase;
    while(kase --) {
        cin >> M;
        vector<Internal> vec;
        int x, y;
        while(cin >> x >> y && (x || y)) vec.push_back(Internal(x, y));

        sort(vec.begin(), vec.end());
        int s = 0, t = M;
        auto L = vec.begin();
        vector<Internal> ans;
        while(s < t && L != vec.end()) {  // 右端点无法覆盖
            auto R = upper_bound(L, vec.end(), Internal(s, s));
            if(L == R) break;  // 左端点无法覆盖
            auto chosen = *max_element(L, R, [](const Internal& a, const Internal& b) {
                return a.r < b.r;
            });
            ans.push_back(chosen);
            L = R, s = chosen.r;
        }

        if(s < t) cout << 0 << endl;  // 无法覆盖完全
        else {
            cout << ans.size() << endl;
            for(const auto& p : ans) cout << p.l << " " << p.r << endl;
        }
        if(kase) cout << "\n";
    }
}

四、归纳

  • 区间覆盖问题:按左端点排序,维护“截取点”,总是选择被截取后剩余长度最长的区间。