区间问题大都是贪心法

最多的不重叠区间数

最早结束时间优先,给其他区间腾出空间。用end记录已选区间的最右端,比较当前区间起点和end,不重叠则可选当前区间。

int findMinArrowShots(vector<vector<int>>& points) {
    // 找最多的不重叠区间数
    sort(points.begin(), points.end(), [](const vector<int> &a, const vector<int> &b) {
        return a[1] < b[1];
    });
    int ans = 0;
    long end = LONG_MIN;
    for (auto &p : points) {
        if (p[0] > end) {
            end = p[1];
            ans++;
        }
    }
    return ans;
}

带权重的区间,总权重最大的不重叠区间

要用动态规划+二分搜索

int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
    // 先按endTime排序
    const int N = startTime.size();
    vector<array<int, 3>> jobs;
    for (int i = 0; i < N; i++) {
        jobs.push_back({ endTime[i], startTime[i], profit[i] });
    }
    sort(begin(jobs), end(jobs));

    // 设dp[endTime]表示到某endTime的maxProfit
    map<int, int> dp; // endTime=>maxProfit
    dp[0] = 0; // 左哨兵
    for (auto& [e, s, p] : jobs) {
        // 对每个job找它前面与它相容的最后一个区间
        // 相容,即toFind.endTime<=cur.startTime
        int preMaxP = prev(dp.upper_bound(s))->second;
        int curMaxP = preMaxP + p;
        // 利润比已知更大,选择当前区间
        if (curMaxP > dp.rbegin()->second) dp[e] = curMaxP;
    }
    return dp.rbegin()->second;
}

尽量多地上课程

课程(t,d)有持续时间t、最晚结束时间d。列举两个课程(a,x),(b,y)比较的所有情况(a+b<=x、x<a+b<=y、y<a+b;a<=b、a>b;假设x<y)可知:最晚结束时间优先总没错。这结论跟“最多的不重叠区间数”一样,尽管这里是最晚结束时间,那里是实际结束时间。

实际结束时间和最晚结束时间的区别在于,若当前课程跟前面区间重叠,实际结束时间的情况下当前课程不能选,最晚结束时间的情况下:当前课程可以替换掉已选课程中持续时间t最大、且t比当前课程更大的某课程,以腾出更多时间。

int scheduleCourse(vector<vector<int>>& courses) {
    sort(courses.begin(), courses.end(), [](const auto &a, const auto &b) {
        return a[1] < b[1];
    });
    priority_queue<int> taken; // 已选课程的持续时间t

    int end = 0; // 已选课程的实际结束时间
    for (auto &c : courses) {
        if (end + c[0] <= c[1]) { // 选课程c
            taken.push(c[0]);
            end += c[0];
        } else if (!taken.empty()) { // 替换掉持续时间t最大且更大的已选课程
            int t = taken.top();
            if (t > c[0]) {
                taken.pop();
                taken.push(c[0]);
                end += c[0] - t;
            }
        }
    }
    return taken.size();
}

为使集合S与每个区间交集>=2,集合S最少要有几个数

int intersectionSizeTwo(vector<vector<int>>& intervals) {
    // 区间按right排序,right相同时优先考虑范围小的
    sort(intervals.begin(), intervals.end(), [](vector<int> &a, vector<int> &b) {
        if (a[1] == b[1]) return a[0] > b[0];
        return a[1] < b[1];
    });

    int ans = 0;
    // last1,last2是集合S[..last2,last1]的最后两个数
    int last1 = INT_MIN, last2 = INT_MIN;
    for (auto &interval : intervals) {
        if (last1 < interval[0]) {
            // S与当前区间不相交,要在S中加入当前区间的最后两个数
            ans += 2;
            last1 = interval[1];
            last2 = last1 - 1;
        } else if (last2 < interval[0]) {
            // S与当前区间有一个数相交,要在S中加入当前区间的最后一个数
            ans += 1;
            last2 = last1;
            last1 = interval[1];
        }
    }
    return ans;
}