暴雨梨花针的最少发射次数 | 豆包MarsCode AI刷题

95 阅读3分钟

题目解析

本文是一篇题解分享,对应题目是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;
}

知识总结

本题是经典的区间合并问题,在排序的基础上通过双指针或贪心策略实现最优解。

学习计划

  • 学习并总结经典的区间问题(如区间覆盖、区间合并、最小区间交集)。
  • 理解双指针的使用场景,练习从数据排序到线性扫描的完整流程。

工具运用

  1. 提供思路:豆包Marscode AI / ChatGPT等
  2. 题库:codeforce / leetcode / 稀土掘金AI刷题等
  3. 学习优秀题解:leetcode官方题解 / B站视频;Github上高star的刷题仓库