【757. 设置交集大小至少为2】

来源:力扣(LeetCode)

描述:

一个整数区间 [a, b] ( a < b ) 代表着从 ab 的所有连续整数,包括 a 和 b。

给你一组整数区间intervals,请找到一个最小的集合 S,使得 S 里的元素与区间intervals中的每一个整数区间都至少有2个元素相交。

输出这个最小集合S的大小。

示例 1:

输入: intervals = [[1, 3], [1, 4], [2, 5], [3, 5]]
输出: 3
解释:
考虑集合 S = {2, 3, 4}. S与intervals中的四个区间都有至少2个相交的元素。
且这是S最小的情况,故我们输出3

示例 2:

输入: intervals = [[1, 2], [2, 3], [2, 4], [4, 5]]
输出: 5
解释:
最小的集合S = {1, 2, 3, 4, 5}.

注意:

  • intervals 的长度范围为[1, 3000]。

  • intervals[i] 长度为 2,分别代表左、右边界。

  • intervals[i][j] 的值是 [0, 108]范围内的整数。

方法:贪心

思路与算法

首先我们从稍简化的情况开始分析:如果题目条件为设置交集大小为 1,为了更好的分析我们将 intervals 按照 [s, e] 序对进行升序排序,其中 intervals 为题目给出的区间集合,s, e 为区间的左右边界。设排序后的 intervals = { [s1 ,e1,] ,…, [sn ,en]},其中 n 为区间集合的大小,并满足 ∀i, j ∈ [1, n], i < jsi ≤ sj 成立。然后对于最后一个区间 [sn, en] 来说一定是要提供一个最后交集集合中的元素,那么我们思考我们选择该区间中哪个元素是最优的——最优的元素应该尽可能的把之前的区间尽可能的覆盖。那么我们选择该区间的开头元素 sn 一定是最优的,因为对于前面的某一个区间 [si, sj]

  • 如果 sj < sn :那么无论选择最后一个区间中的哪个数字都不会在区间 [si, sj] 中。

  • 否则 sj ≥ sn :因为 sn ≥si 所以此时选择 sn 一定会在区间 [si, sj] 中。

即对于最后一个区间 [sn,en] 来说选择区间的开头元素 sn 一定是最优的。那么贪心的思路就出来了:排序后从后往前进行遍历,每次选取与当前交集集合相交为空的区间的最左边的元素即可,然后往前判断前面是否有区间能因此产生交集,产生交集的直接跳过即可。此时算法的时间复杂度为: O(nlogn) 主要为排序的时间复杂度。

那么我们用同样的思路来扩展到需要交集为 m , m > 1 的情况:此时同样首先对 intervals 按照 [s, e] 序对进行升序排序,然后我们需要额外记录每一个区间与最后交集集合中相交的元素(只记录到 m 个为止)。同样我们从最后一个区间往前进行处理,如果该区间与交集集合相交元素个数小于 m 个时,我们从该区间左边界开始往后添加不在交集集合中的元素,并往前进行更新需要更新的区间,重复该过程直至该区间与交集元素集合有 m 个元素相交。到此就可以解决问题了,不过我们也可以来修改排序的规则——我们将区间 [s, e] 序对按照 s 升序,当 s 相同时按照 e 降序来进行排序,这样可以实现在处理与交集集合相交元素个数小于 m 个的区间 [si, ei] 时,保证不足的元素都是在区间的开始部分,即我们可以直接从区间开始部分进行往交集集合中添加元素。而对于本题来说,我们只需要取 m = 2 的情况即可。

代码:

class Solution {
public:
    void help(vector<vector<int>>& intervals, vector<vector<int>>& temp, int pos, int num) {
        for (int i = pos; i >= 0; i--) {
            if (intervals[i][1] < num) {
                break;
            }
            temp[i].push_back(num);
        }
    }

    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        int n = intervals.size();
        int res = 0;
        int m = 2;
        sort(intervals.begin(), intervals.end(), [&](vector<int>& a, vector<int>& b) {
            if (a[0] == b[0]) {
                return a[1] > b[1];
            }
            return a[0] < b[0];
        });
        vector<vector<int>> temp(n);
        for (int i = n - 1; i >= 0; i--) {
            for (int j = intervals[i][0], k = temp[i].size(); k < m; j++, k++) {
                res++;
                help(intervals, temp, i - 1, j);
            }
        }
        return res;
    }
};

执行用时:56 ms, 在所有 C++ 提交中击败了46.22%的用户
内存消耗:20.3 MB, 在所有 C++ 提交中击败了18.49%的用户
复杂度分析
时间复杂度: O(nlogn + nm),其中 n 为给定区间集合 intervals 的大小, m 为设置交集大小,本题为 2。
空间复杂度: O(nm),其中 n 为给定区间集合 intervals 的大小, m 为设置交集的大小,本题为 2。主要开销为存储每一个区间与交集集合的相交的元素的开销。
author:LeetCode-Solution

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值