给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
示例 1:
输入: [ [1,2], [2,3], [3,4], [1,3] ] 输出: 1 解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: [ [1,2], [1,2], [1,2] ] 输出: 2 解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: [ [1,2], [2,3] ] 输出: 0 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
思路:这道题先说解法,首先按照结束时间排序,然后遍历数组,先把第一个元素加进答案中,然后遍历后面的元素,如果后面元素的起始时间和截止时间包含第一个元素,就跳过。否则就把元素加入答案。伪代码如下:
Sort jobs by finish times so that f1 <= f2 <= ... <= fn.
select a task ——>A
for j = 1 to n {
if (job j compatible with A)
A and {j} ——> A
}
return A
其实贪心算法如果简单的题好理解(比如01背包问题),但是对于这类直接给出结论的确实让人难以理解,为什么那么多种其他组合,这种解法就能得到最优解呢?下面给出证明:
首先,该算法选出的区间是互不重叠的。
其次,设fi为该算法所接受的第i个区间的右端点坐标,gi为某最优解中的第i个区间的右端点坐标。
命题1.1 当i>=1时,该算法所接受的第i个区间的右端点坐标fi<=某最优解中的第i个区间的右端点坐标gi。
该命题可以运用数学归纳法来证明。对于i=1,命题显然为真,因为算法第一个选择的区间拥有最小右端点坐标。令i>1,假定论断对i-1为真,即fi-1<=gi-1。则最优解的第i个可选区间所组成的集合包含于执行该算法时第i个可选区间所组成的集合;而当算法选择第i个区间时,选的是在可选区间中右端点坐标最小的一个(意思就是说对于最优解而言,第i个区间可以选择的区间是一个集合,不只一个,而该算法选择的是右边结束时间最少的那个区间(集合中的区间的其中一个),),所以有fi<=gi。证毕。
设该算法选出了k个区间,而最优解选出了m个区间。
命题1.2 最优解选出的区间数量m=该算法选出的区间数量k。
假设m>k,根据命题1.1,有fk<=gk。由于m>k,必然存在某区间,在gk之后开始,故也在fk之后开始。而该算法一定不会在选了第k个区间后停止,还会选择更多的区间,产生矛盾。所以m<=k,又因为m是最优解选出区间个数,所以m>=k。,因而m=k.
综上所述,算法选出的区间是最优解。
参考代码:
/**
* Definition for an interval.
* struct Interval {
* int start;
* int end;
* Interval() : start(0), end(0) {}
* Interval(int s, int e) : start(s), end(e) {}
* };
*/
class Solution {
public:
static bool mycmp(const Interval &a, const Interval &b) {
return a.end < b.end;
}
int eraseOverlapIntervals(vector& intervals) {
if (intervals.size() == 0) return 0;
int count=1;
sort(intervals.begin(), intervals.end(), mycmp);
Interval lastInterval=intervals[0];
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i].start < lastInterval.end && lastInterval.end <= intervals[i].end) continue;
else lastInterval = intervals[i], count++;
}
return intervals.size() - count;
}
};