用最少数量的箭引爆气球(贪心,区间)
有一些球形气球贴在一堵用\(XY\)平面表示的墙面上。墙面上的气球记录在整数数组\(points\),其中\(points[i] = [x_{start}, x_{end}]\)表示水平直径在\(x_{start}\)和\(x_{end}\)之间的气球。你不知道气球的确切\(y\)坐标。
一支弓箭可以沿着\(x\)轴从不同点完全垂直地射出。在坐标\(x\)处射出一支箭,若有一个气球的直径的开始和结束坐标为\(x_{start}\),\(x_{end}\), 且满足\(x_{start} ≤ x ≤ x_{end}\),则该气球会被 引爆 。可以射出的弓箭的数量 没有限制。弓箭一旦被射出之后,可以无限地前进。
给你一个数组\(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支箭。
示例 3:
输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:气球可以用2支箭来爆破:
- 在x = 2处发射箭,击破气球[1,2]和[2,3]。
- 在x = 4处射出箭,击破气球[3,4]和[4,5]。
提示:
\(1 <= points.length <= 10^5\)
\(points[i].length == 2\)
\(-2^{31} <= x_{start} < x_{end} <= 2^{31} - 1\)
我们把气球看做区间,箭还是箭,箭是垂直向上射出。
首先,对于右端点最小的那个区间,如果想要用箭穿过它,那么一定从它的右端点穿过(从右端点穿过才只会穿过更多的区间)。
接下来,对于这只箭能未能穿过的区间,再从中找出右端点最小的区间。对于这只箭未能穿过的区间,如此往复的找下去。最终我们使用的箭数就是最少的。
------------------------------------------------图1--------------------------------
如何寻找第一个右端点最小的区间,以及在未被第一支箭穿过的剩余区间中,继续寻找第一个右端点最小的区间呢?
当我们把每个区间按照右端点升序排序后,显然第一个区间就是我们最开始要找的第一个区间,后序也可以进一步找到满足条件的最小右端点区间。具体的过程如下:
首先将区间按照右端点升序排序,此时位序为1的区间就是我们要找的第一个区间(图1),我们需要记录下第一个区间的右端点right(射出第一支箭),然后继续遍历,此时就会存在两种情况:
对于左端点小于等于right的区间,说明该区间能被前面的箭(right)穿过。
对于接下来左端点大于right的区间,说明前面这支箭无法穿过该区间(即:该区间就是未被箭穿过的区间集合的第一个区间),我们又找到了第一个未被箭穿过的区间,此时我们用一把新的箭穿过该区间的右端点(即更新right:right = points[i][1]),并将使用的箭数+1。如此往复。
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
int n=points.size();
if(n==0){
return 0;
}
sort(points.begin(),points.end(),[&](vector<int> a,vector<int> b){
return a[1]<b[1];
});
int result = 1;
// 第一支箭直接射出
int arrow = points[0][1];
for(int i=1;i<n;i++){
if(points[i][0]<=arrow){
continue;
}
arrow=points[i][1];
result++;
}
return result;
}
};