区间贪心问题合集

Posted 白龙码~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区间贪心问题合集相关的知识,希望对你有一定的参考价值。

区间贪心问题

LeetCode 56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

两个区间能够重叠,当且仅当:区间B的start小于等于区间A的end。

设ABC三个区间分别为[startA, endA]、[startB, endB]、[startC, endC],假设A与B能合并,与C不能合并,则:

startB <= endA,startC > endB >= startB

因此startA、startB、startC满足:startA < startB < startC或startB < startA < startC,换个角度考虑,能够合并的区间,它们的起始点start必然是相邻的。

因此,可以将所有区间按照start进行排序,并贪心地合并相邻区间即可。

vector<vector<int>> merge(vector<vector<int>>& intervals) 

    sort(intervals.begin(), intervals.end());
    vector<vector<int>> res;
    int x = -1;
    int y = -1;
    for (auto& interval : intervals)
    
        if (x == -1 && y == -1)
        
            x = interval[0];
            y = interval[1];
            continue;
        
        if (interval[0] <= y) // 当前区间与前一个区间重合,此时更新前一个区间的end
        
        	y = max(interval[1], y);
        
        else // 区间不重合,将前一个区间插入结果集,同时更新前一个区间为当前区间
        
            res.push_back(x, y);
            x = interval[0];
            y = interval[1];
        
    
    res.push_back(x, y);
    return res;

LeetCode 57. 插入区间

给你一个 无重叠的 ,按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

由于区间已经排序好,因此只需要初始化前一个区间为待插入的区间,并顺序遍历,尝试使用该区间合并其他区间。

vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) 

    int n = intervals.size();
    vector<vector<int>> res;
    int x = newInterval[0];
    int y = newInterval[1]; 
    for (int i = 0; i < n; ++i)
    
        if (intervals[i][0] > y) // 找到了插入点,此时更新待插入的区间
        
            res.push_back(x, y);
            x = intervals[i][0];
            y = intervals[i][1];
        
        else if (intervals[i][1] < x) // 还没有找到合适的插入区间
        
            res.push_back(intervals[i]);
        
        else // 区间可合并
        
            x = min(x, intervals[i][0]);
            y = max(y, intervals[i][1]);
        
    
    res.push_back(x, y);
    return res;

LeetCode 435. 无重叠区间

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。

返回 需要移除区间的最小数量,使剩余区间互不重叠 。

区间A与区间B重叠,当且仅当startB<endA。

我们将区间按照start进行升序排列。当区间重叠时,此时需要移除的区间必然是end值较小的那一个区间,因为这样能够尽可能地减少当前区间与剩余区间重叠的可能性。

具体算法为:记录上一个区间的preend,然后遍历所有区间。

  • 当发生重叠时,选择end值较小的那一个更新preend,同时res++。
  • 没有重叠时,只需动态更新preend为当前已选区间的end最大值即可,不需要res++。
  • 最后res的值就是移除区间的最小数量。
int eraseOverlapIntervals(vector<vector<int>>& intervals) 

    sort(intervals.begin(), intervals.end());
    int y = intervals[0][1];
    int res = 0;
    for (int i = 1; i < intervals.size(); ++i)
    
        // 重叠
        if (intervals[i][0] < y)
        
            ++res;
            y = min(intervals[i][1], y);
        
        else
        
            y = max(intervals[i][1], y);
        
    
    return res;

LeetCode 2406. 将区间分为最少组数

给你一个二维整数数组 intervals ,其中 intervals[i] = [lefti, righti] 表示闭区间[lefti, righti] 。

你需要将 intervals 划分为一个或者多个区间组,每个区间只属于一个组,且同一个组中任意两个区间不相交 。

请你返回最少需要划分成多少个组。

如果两个区间覆盖的范围有重叠(即至少有一个公共数字),那么我们称这两个区间是相交的。比方说区间 [1, 5] 和 [5, 8] 相交。

如果区间B的start小于等于区间A的end,则A与B能合并,即它们不能在同一组。

因此,我们维护每一个分组的最大end的小根堆:

  • 如果intervals[i]的start小于等于堆顶元素,则表明它的start小于等于当前分组内任意一个分组的最大end,因此该区间只能单独成一组。
  • 如果intervals[i]的start大于堆顶元素,则该区间至少可以添加至堆顶这个分组,因此更新这个分组的最大end为intervals[i]的end即可。

最终,堆中元素的数量就是组数。

int minGroups(vector<vector<int>>& intervals) 

    sort(intervals.begin(), intervals.end());
    priority_queue<int, vector<int>, greater<int>> end;
    for (auto& interval : intervals)
    
        if (!end.empty() && interval[0] > end.top())
        	end.pop();
        end.push(interval[1]);
    
    return end.size();

以上是关于区间贪心问题合集的主要内容,如果未能解决你的问题,请参考以下文章

贪心算法第五篇: 区间问题(右边界总是大于左边界-隐含条件)

贪心算法第五篇: 区间问题(右边界总是大于左边界-隐含条件)

51node1091(贪心)

HDU4343Interval query 倍增

一些经典贪心模板

贪心算法2无重叠区间(中等)