题目解析
本文是一篇题解分享,对应题目是AI刷题板块的中等题-《唐门绝技:暴雨梨花针的最少发射次数》。 问题简述如下:
唐门暴雨梨花针能在一条直线上摧毁所有目标。设置了若干个靶子在二维平面上。每个靶子是一条垂直于X轴的线段,由三个参数 x_left,x_right,y 描述,其中 y 是固定的高度,x_left 和 x_right 表示线段在X轴上的起止位置。使用尽可能少的暴雨梨花针来击中所有靶子,且每次射击必须经济有效。每次射击暴雨梨花针都将从某个 x 值发射,且保证靶子的高度在暴雨梨花针的可达范围。请你计算最少需要多少次射击,才能保证击中所有靶子。结果需对给定的 P 取余。
思路分析
为了解决这道题,我们可以采取如下思路:
-
问题转化:题目中的靶子可以抽象为区间 [x_left, x_right],目标是最小化这些区间所需的覆盖数。因此,我们可以将问题转化为一个“区间合并”问题:如何将重叠区间归并为最小集合,同时记录需要射击的次数。
-
排序简化:通过将所有区间按照起始位置 x_left 升序排序,可以确保遍历的过程中区间是按照从左到右的顺序处理的,这样便于使用双指针法解决问题。
-
双指针法:用两个指针 left 和 right 表示当前合并区间的左右边界。遍历区间数组时:
- 如果下一个区间的起点 x_left 在当前合并区间 right 之外,则说明需要新增一次射击,并更新新的合并区间。
- 如果下一个区间的终点 x_right 小于当前合并区间的 right,则缩小当前区间的右边界,选择更小的终点进行更新,从而保证区间尽量短。
- 重复以上操作直到覆盖所有区间。
-
结果计算:最后统计合并过程中新增区间的次数,即为需要的最少射击次数,并对给定的 P 取模以获得最终结果
代码逻辑
根据上文的思路分析,关键代码如下
if(target[index][0] > right){ // 完全不粘连
res++;
left = target[index][0];
right = target[index][1];
}
else if(target[index][1] <= right){
// 当前区间的右边界更小,保留最小右边界
right = target[index][1];
}
// else - 当前区间包含了右边界
完整代码
完整代码如下
public static int solution(int k, int p, int[][] target) {
// 基本思路:双指针
// 先将target按照左边界升序排列
Arrays.sort(target, (a, b) -> {
return a[0] - b[0];
});
int left = target[0][0];
int right = target[0][1];
int res = 1;
int index = 1;
while(index < target.length){
if(target[index][0] > right){
// 完全不粘连
res++;
left = target[index][0];
right = target[index][1];
}
else if(target[index][1] <= right){
// 当前区间的右边界更小,保留最小右边界
right = target[index][1];
}
// else - 当前区间包含了右边界
index++;
}
return res % p;
}
知识总结
本题是经典的区间合并问题,在排序的基础上通过双指针或贪心策略实现最优解。
学习计划
- 学习并总结经典的区间问题(如区间覆盖、区间合并、最小区间交集)。
- 理解双指针的使用场景,练习从数据排序到线性扫描的完整流程。
工具运用
- 提供思路:豆包Marscode AI / ChatGPT等
- 题库:codeforce / leetcode / 稀土掘金AI刷题等
- 学习优秀题解:leetcode官方题解 / B站视频;Github上高star的刷题仓库