给定一个区间的集合,找到须要移除区间的最小数量,使剩余区间互不重叠。算法
注意:数组
示例 1:code
输入: [ [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 解释: 你不须要移除任何区间,由于它们已是无重叠的了。
思路:这道题先说解法,首先按照结束时间排序,而后遍历数组,先把第一个元素加进答案中,而后遍历后面的元素,若是后面元素的起始时间和截止时间包含第一个元素,就跳过。不然就把元素加入答案。伪代码以下:it
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背包问题),可是对于这类直接给出结论的确实让人难以理解,为何那么多种其余组合,这种解法就能获得最优解呢?下面给出证实:io
首先,该算法选出的区间是互不重叠的。ast
其次,设fi为该算法所接受的第i个区间的右端点坐标,gi为某最优解中的第i个区间的右端点坐标。class
命题1.1 当i>=1时,该算法所接受的第i个区间的右端点坐标fi<=某最优解中的第i个区间的右端点坐标gi。select
该命题能够运用数学概括法来证实。对于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<Interval>& 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; } };