今天继续学习贪心算法解决相关问题。
452.用最少数量的箭引爆气球
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
示例 1:
输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
-在x = 11处发射箭,击破气球[10,16]和[7,12]。
示例 2:
输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4
解释:每个气球需要射出一支箭,总共需要4支箭。
思路:
1.本题思路很好想,局部最优即让一支箭尽可能多的射爆气球,进而达到全局最优的最少弓箭。要让一支箭尽可能多的射爆气球那么就得判断气球的重叠区间,可以很容易想到如果当前气球的左区间小于等于前一个气球的右区间那么就说明一支箭能够同时射穿这两个气球;反之则只能再额外用一支箭。
2.如果当前气球与前一个气球重叠,我们需要更新一下右边界,那么右边界取二者的最大值还是最小值呢?显然如果取最大值的话那么较小的那一个气球有可能不会被射爆,因此我们应当取最小值,然后进一步判断后面气球的左区间与当前右区间的大小关系。
class Solution {
public:
//按照xstart从小到大排序
static bool cmp(const vector<int>&a, const vector<int>&b){
return a[0] < b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
int result = 1;
sort(points.begin(), points.end(), cmp);
if(points.size() == 1) return result;
for(int i = 1; i < points.size(); i++){
//如果当前气球左边界大于前一个气球右边界,只能再用一支箭
if(points[i][0] > points[i - 1][1]){
result++;
}
//当前气球左边界小于等于前一个气球右边界,能用一支箭射穿
else{
points[i][1] = min(points[i][1], points[i - 1][1]);
}
}
return result;
}
};
启发:
1.一开始关于要更新右边界的地方确实不好想,个人最初只想到了如果有重叠就不需要再加一支箭,直接进行接下来的比较,但又遗忘了一个问题:如果不更新右区间,那么当前气球与下一个凄气球或许会重叠,但此时又不能保证前一个气球处于重叠的区间内了,即不能保证一支箭射穿这三个气球,此时要么保证前两个,要么又保证后两个。这样一来问题就复杂了,又陷入到了一种顾此失彼的状态。因此更新右边界很重要,我们直接保证当前已经判断的前一个气球和当前气球一定能被一支箭射爆!
2.本题没有必要真的去模拟射爆气球的过程,把气球从数组中移除,这样反而复杂了,我们只需要用一个结果变量存储需要的箭,再进行气球的重叠判断时增加结果变量即可。
435.无重叠区间
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
示例 1:
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
思路:
1.有了上一题的基础,本题思路就很好想了。依旧是通过比较当前区间左区间与上一个区间的右区间大小进行判断。不过需要注意的是如果二者相等并不算重叠,只有当前者小于后者时才算重叠。
class Solution {
public:
//左区间由小到大排序
static bool cmp(const vector<int>& a, const vector<int>& b){
return a[0] < b[0];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
int result = 0;
if(intervals.size() == 1) return result;
sort(intervals.begin(), intervals.end(), cmp);
for(int i = 1; i < intervals.size(); i++){
//当前区间左边界小于上一个区间的右边界,发生重叠
if(intervals[i][0] < intervals[i - 1][1]){
//更新此时的右边界为二者右边界最小值,进行结果计数
intervals[i][1] = min(intervals[i][1],intervals[i - 1][1]);
result++;
}
}
return result;
}
};
启发:
1.重叠区间问题一定要搞清楚重叠的条件,判断好重叠后的操作。