题目重点:对闭区间气球选择最少射击位置,使用最早结束点覆盖,并处理相接端点。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 每气球为闭区间[left,right],left≤right;一箭任意实数x,覆盖所有包含x的区间。输入不修改,空列表0。
- 与半开区间会议调度不同,left等于已有射点时仍被覆盖。
解题路线
- 按right升序,遇第一个尚未被覆盖区间时,在其right射箭;随后所有left≤该射点的区间都覆盖,不需要更新已有射点。
- 交换证明:任何方案为最早结束区间射的某箭x≤right,把它移到right,不会失去对尚未处理区间的覆盖,因为其他区间right≥该right且若原包含x,它们left≤x≤right。因此最右允许点至少同样好。
- 区间left>currentArrow才新开箭;相等不新开。多个相同right自然一箭,点区间[left=right]合法。
- 排序是主要成本;不要按left分组后只延长当前right,那容易把交集变并集,错误认为一箭覆盖没有公共点的链。
手动走一遍
[1,2]、[2,3]一箭x2覆盖两者;[1,2]、[3,4]需2箭。[1,3]、[2,4]、[4,5]前两可x3,第三需新箭,不能因为区间链连通就判1。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def minimum_arrows(intervals):
"""Closed intervals, arbitrary real coordinate arrows; input unmodified."""
if any(a>b for a,b in intervals):raise ValueError('reversed interval')
end=None;arrows=0
for left,right in sorted(intervals,key=lambda p:p[1]):
if end is None or left>end:
end=right;arrows+=1
return arrows
复杂度
O(N log N)排序、O(N)扫描;本实现sorted新建O(N)辅助空间,若输入可原地排序,另计排序算法辅助空间。整数端点的测试可穷举所有端点射击子集做独立最优对照。
易错点与追问
容易失分的边界
- 用left≥arrow判新箭会把相接闭区间误判。
- 连通并集不代表所有区间有共同交点。
继续深挖
- 若射击位置只能取某个候选集合,最早结束区间的右端未必可选,应怎样选可行点?
- 每箭有不同成本时,贪心最少数量还能保证最小成本吗?
