当前位置:   article > 正文

[Leetcode力扣 452] Minimum Number of Arrows to Burst Balloons_几箭射中所有气球

几箭射中所有气球

一、题目描述

给一堆二维闭区间表示气球,问最少用几支箭能把所有气球射爆

二、分析:戳气球问题等价于“不相交区间问题”

先介绍“不相交区间问题”:给出n个区间,从中挑选出尽可能多的区间,使这些区间两两没有交集。然后下个结论:戳气球问题等价于不相交区间问题,下面介绍为啥:

“不相交区间问题”找到的是n个区间中互不相交的区间,假设找到了k个不相交区间,那么必须有k支箭来射爆这些区间代表的气球;并且对于这每一个不相交区间,可能都会有与其重叠的其他区间存在,那么这k支箭中的每一支都可以找到一个合适的位置,射爆与每一个不相交区间相交的区间代表的气球,所以最少需要k支箭来射爆所有的气球。

还可以用证明一下上面的结论:假设需要k+1支箭才能射爆所有气球(易知至少需要k支箭,所以不需要假设k-1支箭的情况),那么多出来了一个区间(也就是气球),假设多出来的这个区间是一个不相交区间,那么和有k个不相交区间这一条件相悖,不可能;假设多出来的这个区间与某个区间相交,那么它一定和k个区间中的某一个相交,那么也不需要额外的一支箭。综上,想要射爆所有气球,那么最少使用k支箭即可。

在理解上还有一个小问题:“不相交区间问题”找到的是最多的不相交区间,而戳气球问题却要求我们找到戳爆所有气球的最少的箭数,这一个问题是“找最多”,另一个是“找最少”,这两个问题为啥会等价呢?其实,两个问题各自要找的“多少”的含义是截然不同的:区间问题中要求“最多的”不相交区间,是为了这个结果能够包括整个区间集合,试想,如果不要求“最多”,那么不相交区间永远选0个或者1个不就行了;而戳气球问题选“最少”是因为,如果选最多,那么总选n个不就行了。综上,这两个问题各自定义的“多少”含义完全不同,所以没必要在这上面纠结。

三、解决“不相交区间问题”的思路

(这里照搬一下《算法笔记》中提供的思路)

关键在于两点:(1)如果出现“区间包含”的情况,如图a所示,那么一定要选择被包含的区间,因为这样就有更大的空间容纳别的区间了,正所谓贪心的思想正体现在这里。(2)对于图b的情况,要能找到“铁定不和其他区间相交的部分”,比如说图中x1的虚线的右半部分,然后x1剩下的部分就变成了(1)的情况,那么选哪个就很简单了。

要达到(2)的效果,对某个区间端点进行排序即可,无论排序的是左端点还是右端点,是从大到小排还是从小到大排,都可以。下面以对区间左端点从小到大排序为例:

四、代码实现:对区间左端点从小到大排序

  1. class Solution {
  2. public:
  3. int findMinArrowShots(vector<vector<int>>& points)
  4. {
  5. if(points.empty()) return 0;
  6. sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
  7. return a[0] < b[0];
  8. });
  9. //right代表当前选择的区间的右边界,res是不相交区间个数
  10. int n = points.size(), right = points[0][1], res = 1;
  11. for(int i = 1; i < n; ++i)
  12. {
  13. //如果当前区间的左边界大于当前选择的区间的右边界,
  14. //说明我们又找到了一个不相交区间,并且right值要更新为该区间的右边界
  15. if(points[i][0] > right)
  16. {
  17. ++res;
  18. right = points[i][1];
  19. }
  20. //如果当前区间的左边界小于等于当前选择的区间的右边界,
  21. //说明当前区间铁定不是一个不相交区间,但是right值可能需要更新,
  22. //因为可能出现当前区间的右边界值小于right,此时应该选择更小的right值
  23. else
  24. right = right < points[i][1] ? right : points[i][1];
  25. }
  26. return res;
  27. }
  28. };

五、还有其余多种排序方法

1. 对区间左端点从大到小排序:

  1. class Solution {
  2. public:
  3. int findMinArrowShots(vector<vector<int>>& points)
  4. {
  5. if(points.empty()) return 0;
  6. sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
  7. return a[0] > b[0];
  8. });
  9. int n = points.size(), right = points[n-1][1], res = 1;
  10. for(int i = n - 2; i >= 0; --i)
  11. {
  12. if(points[i][0] > right)
  13. {
  14. ++res;
  15. right = points[i][1];
  16. }
  17. else
  18. right = right < points[i][1] ? right : points[i][1];
  19. }
  20. return res;
  21. }
  22. };

2. 对区间右端点从小到大排序

  1. class Solution {
  2. public:
  3. int findMinArrowShots(vector<vector<int>>& points)
  4. {
  5. if(points.empty()) return 0;
  6. sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
  7. return a[1] < b[1];
  8. });
  9. int n = points.size(), left = points[n-1][0], res = 1;
  10. for(int i = n - 2; i >= 0; --i)
  11. {
  12. if(points[i][1] < left)
  13. {
  14. ++res;
  15. left = points[i][0];
  16. }
  17. else
  18. left = left > points[i][0] ? left : points[i][0];
  19. }
  20. return res;
  21. }
  22. };

3. 对区间右端点从大到小排序

  1. class Solution {
  2. public:
  3. int findMinArrowShots(vector<vector<int>>& points)
  4. {
  5. if(points.empty()) return 0;
  6. sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
  7. return a[1] > b[1];
  8. });
  9. int n = points.size(), left = points[n-1][0], res = 1;
  10. for(int i = 0; i < n; ++i)
  11. {
  12. if(points[i][1] < left)
  13. {
  14. ++res;
  15. left = points[i][0];
  16. }
  17. else
  18. left = left > points[i][0] ? left : points[i][0];
  19. }
  20. return res;
  21. }
  22. };

 

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/717708
推荐阅读
相关标签
  

闽ICP备14008679号