65.唐门绝技:暴雨梨花针最少发射次数|Marscode AI刷题

1.题目

问题描述

在唐门,外门弟子唐三发现了一种强大的暗器技巧,名为暴雨梨花针,它能在一条直线上摧毁所有目标。唐三在一本古籍中学习到了这门技巧,为了练习,他设置了若干个靶子在二维平面上。每个靶子是一条垂直于X轴的线段,由三个参数 x_{left}x_{right}y 描述,其中 y 是固定的高度,x_{left} 和 x_{right} 表示线段在X轴上的起止位置。

唐三的目标是使用尽可能少的暴雨梨花针来击中所有靶子。由于暴雨梨花针的珍贵,每次射击必须经济有效。每次射击暴雨梨花针都将从某个 x 值发射,能垂直上升到 y = 100

请你帮助唐三计算最少需要多少次射击,才能保证击中所有靶子。结果需对给定的 P 取余。


测试样例

样例1:

输入:k = 4, p = 100, target = [[10, 26, 3], [4, 8, 29], [1, 5, 8], [9, 9, 9]]

输出:3

样例2:

输入:k = 3, p = 100, target = [[10, 26, 3], [4, 8, 29], [1, 5, 8]]

输出:2

样例3:

输入:k = 5, p = 100, target = [[5, 15, 5], [1, 2, 3], [20, 25, 2], [6, 18, 2], [30, 40, 1]]

输出:4

样例4:

输入:k = 2, p = 100, target = [[0, 10, 5], [15, 20, 3]]

输出:2

样例5:

输入:k = 6, p = 100, target = [[3, 8, 10], [5, 10, 12], [1, 3, 14], [15, 18, 10], [20, 25, 10], [8, 12, 10]]

输出:4

2.样例分析

暴雨梨花针能在一条直线上摧毁所有目标

样例 1 最少暴雨梨花针数量: 3

样例 2 最少暴雨梨花针数量: 2

3.思路

这道题和435. 无重叠区间 - 力扣(LeetCode)非常相似,重叠的区间用一根暴雨梨花针。

  • 排序靶子: 首先,我们将所有的靶子按其左边界进行排序,如果左边界相同,则按右边界排序。这一步是为了确保在射击时能够尽量减少箭矢的数量。
  • 贪心策略: 我们初始化箭矢的范围为第一个靶子的范围(即左边界和右边界)。然后遍历其余靶子,如果当前靶子的左边界小于等于当前射击范围的右边界(即可以用当前箭矢射中该靶子),则更新当前箭矢的范围。如果当前靶子的左边界大于当前射击范围的右边界,说明需要使用新的箭矢,增加箭矢的数量并更新射击范围。
  • 计算箭矢数量: 最后,返回箭矢数量对 p 的余数。

4.代码

绘制靶子图形

import matplotlib.pyplot as plt

# 设置 matplotlib 支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

def minimum_arrows(targets):
    if not targets:
        return 0
    # 排序:首先按右边界排序,再按y坐标排序
    targets.sort(key=lambda x: (x[1], x[2]))  # 排序根据右边界和y坐标
    arrows = 1
    end = targets[0][1]
    for target in targets[1:]:
        if target[0] > end:
            arrows += 1
            end = target[1]
    return arrows

def plot_targets(targets, title):
    plt.figure()
    for target in targets:
        x_left, x_right, y = target
        # 如果是点(x_left == x_right),绘制一个小的圆点
        if x_left == x_right:
            plt.plot(x_left, y, 'ro')  # 红色圆点表示点
        else:
            plt.plot([x_left, x_right], [y, y], 'b-')  # 绘制靶子线段
    plt.xlabel('X 轴')
    plt.ylabel('Y 轴')
    plt.title(title)
    plt.grid(True)
    plt.show()

# 样例数据
samples = [
    {
        "k": 4,
        "p": 100,
        "targets": [[10, 26, 3], [4, 8, 29], [1, 5, 8], [9, 9, 9]],
        "title": "样例 1"
    },
    {
        "k": 3,
        "p": 100,
        "targets": [[10, 26, 3], [4, 8, 29], [1, 5, 8]],
        "title": "样例 2"
    },
    {
        "k": 5,
        "p": 100,
        "targets": [[5, 15, 5], [1, 2, 3], [20, 25, 2], [6, 18, 2], [30, 40, 1]],
        "title": "样例 3"
    },
    {
        "k": 2,
        "p": 100,
        "targets": [[0, 10, 5], [15, 20, 3]],
        "title": "样例 4"
    },
    {
        "k": 6,
        "p": 100,
        "targets": [[3, 8, 10], [5, 10, 12], [1, 3, 14], [15, 18, 10], [20, 25, 10], [8, 12, 10]],
        "title": "样例 5"
    }
]

for sample in samples:
    k = sample["k"]
    p = sample["p"]
    targets = sample["targets"]
    title = sample["title"]

    # 计算最少的针数量
    result = minimum_arrows(targets) % p
    print(f"{title} 最少暴雨梨花针数量: {result}")

    # 绘制靶子图
    plot_targets(targets, title)

算法

import java.util.Arrays;
public class Main {
    public static int solution(int k, int p, int[][] target) {
        // Please write your code here、
        // 贪心算法:首先按靶子的左边界(x_left)排序,如果左边界相同,则按右边界(x_right)排序
        Arrays.sort(target, (o1, o2) -> {
            if (o1[0] > o2[0]) {
                return 1; // 如果o1的左边界大于o2的左边界,o1排在后面
            } else if (o1[0] < o2[0]) {
                return -1;
            } else {
                return o1[1] - o2[1]; // 如果左边界相同,则按右边界排序
            } 
        });
        // 初始化:第一个靶子的位置,使用第一支箭矢,范围从左到右
        int l = target[0][0], r = target[0][1], result = 1; // `result`记录使用的箭矢数量
        // 从第二个靶子开始,遍历所有靶子,判断是否能用当前的箭矢覆盖
        for (int i = 1; i < k; i++) {
            // 如果当前靶子的左边界在当前射击范围内,说明可以用当前箭矢继续覆盖
            if (target[i][0] <= r) {
                // 更新当前箭矢的覆盖范围:新的左边界是两个靶子左边界的较大值,右边界是两个靶子右边界的较小值
                l = Math.max(l, target[i][0]);
                r = Math.min(r, target[i][1]);
            } else {
                // 如果当前靶子不能被当前箭矢覆盖,增加一支新的箭矢,并更新射击范围
                result++;
                // 新的箭矢需要从当前靶子的左边界射出,并以当前靶子的右边界为范围
                l = target[i][0];
                r = target[i][1];
            }
        }
        return result % p;
    }

    public static void main(String[] args) {
        //  You can add more test cases here
        int[][] testTarget1 = {
                {10, 26, 3},
                {4, 8, 29},
                {1, 5, 8},
                {9, 9, 9}
        };
        int[][] testTarget2 = {
                {10, 26, 3},
                {4, 8, 29},
                {1, 5, 8}
        };
        int[][] testTarget3 = {
                {13, 20, 2},
                {15, 39, 3},
                {34, 89, 6},
                {2, 10, 1},
                {0, 87, 2},
                {23, 49, 3},
                {2, 45, 9},
                {9, 98, 0},
                {3, 12, 9},
                {35, 45, 21},
                {51, 67, 23},
                {37, 42, 54},
                {55, 76, 7},
                {2, 13, 6},
                {29, 31, 9},
                {10, 32, 1}
        };

        System.out.println(solution(4, 100, testTarget1) == 3);
        System.out.println(solution(3, 100, testTarget2) == 2);
        System.out.println(solution(16, 100, testTarget3) == 5);
    }
}

return o1[1] - o2[1];

  • 返回值为负:当 o1[1] 小于 o2[1] 时,o1 会排在 o2 前面。
  • 返回值为正:当 o1[1] 大于 o2[1] 时,o1 会排在 o2 后面。
  • 返回值为零:当 o1[1] 等于 o2[1] 时,它们的顺序保持不变。

5.参考资料

MarsCode刷题--65.唐门绝技:暴雨梨花针最少发射次数_唐门绝技:暴雨梨花针最少发射次数 问题描述 在唐门,外门弟子唐三发现了一种强大的-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值