赞
踩
给一堆二维闭区间表示气球,问最少用几支箭能把所有气球射爆
先介绍“不相交区间问题”:给出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)的效果,对某个区间端点进行排序即可,无论排序的是左端点还是右端点,是从大到小排还是从小到大排,都可以。下面以对区间左端点从小到大排序为例:
- class Solution {
- public:
- int findMinArrowShots(vector<vector<int>>& points)
- {
- if(points.empty()) return 0;
-
- sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
- return a[0] < b[0];
- });
-
- //right代表当前选择的区间的右边界,res是不相交区间个数
- int n = points.size(), right = points[0][1], res = 1;
- for(int i = 1; i < n; ++i)
- {
- //如果当前区间的左边界大于当前选择的区间的右边界,
- //说明我们又找到了一个不相交区间,并且right值要更新为该区间的右边界
- if(points[i][0] > right)
- {
- ++res;
- right = points[i][1];
- }
- //如果当前区间的左边界小于等于当前选择的区间的右边界,
- //说明当前区间铁定不是一个不相交区间,但是right值可能需要更新,
- //因为可能出现当前区间的右边界值小于right,此时应该选择更小的right值
- else
- right = right < points[i][1] ? right : points[i][1];
- }
- return res;
- }
- };
1. 对区间左端点从大到小排序:
- class Solution {
- public:
- int findMinArrowShots(vector<vector<int>>& points)
- {
- if(points.empty()) return 0;
-
- sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
- return a[0] > b[0];
- });
- int n = points.size(), right = points[n-1][1], res = 1;
- for(int i = n - 2; i >= 0; --i)
- {
- if(points[i][0] > right)
- {
- ++res;
- right = points[i][1];
- }
- else
- right = right < points[i][1] ? right : points[i][1];
- }
- return res;
- }
- };
2. 对区间右端点从小到大排序
- class Solution {
- public:
- int findMinArrowShots(vector<vector<int>>& points)
- {
- if(points.empty()) return 0;
-
- sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
- return a[1] < b[1];
- });
- int n = points.size(), left = points[n-1][0], res = 1;
- for(int i = n - 2; i >= 0; --i)
- {
- if(points[i][1] < left)
- {
- ++res;
- left = points[i][0];
- }
- else
- left = left > points[i][0] ? left : points[i][0];
- }
- return res;
- }
- };
3. 对区间右端点从大到小排序
- class Solution {
- public:
- int findMinArrowShots(vector<vector<int>>& points)
- {
- if(points.empty()) return 0;
-
- sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b){
- return a[1] > b[1];
- });
- int n = points.size(), left = points[n-1][0], res = 1;
- for(int i = 0; i < n; ++i)
- {
- if(points[i][1] < left)
- {
- ++res;
- left = points[i][0];
- }
- else
- left = left > points[i][0] ? left : points[i][0];
- }
- return res;
- }
- };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。