最少数量线段覆盖 - 华为OD统一考试(Java题解)

华为OD机试题库《C++》限时优惠 9.9

华为OD机试题库《Python》限时优惠 9.9

华为OD机试题库《JavaScript》限时优惠 9.9

针对刷题难,效率慢,我们提供一对一算法辅导, 针对个人情况定制化的提高计划(全称1V1效率更高)。

看不懂有疑问需要答疑辅导欢迎私VX: code5bug

华为OD机试真题

题目描述

给定坐标轴上的一组线段,线段的起点和终点均为整数并且长度不小于1,请你从中找到最少数量的线段,这些线段可以覆盖住所有线段。

输入描述

  • 第一行输入为所有线段的数量,不超过10000,
  • 后面每行表示一条线段,格式为”x,y",x和y分别表示起点和终点,取值范围是[-105,105]

输出描述

最少线段数量,为正整数

示例1

输入:
3
1,7
3,6
8,9

输出:
2

说明:
选取2条线段[1,7]和[8,9]即可,这两条线段可以覆盖[3,6]。

示例2

输入:
4
1,4
2,5
3,6
10,20

输出:
3

示例3

输入:
4
1,4
2,5
3,6
1,10

输出:
1

题解

这道题目属于贪心算法区间覆盖问题。我们需要找到最少数量的线段,使得这些线段能够覆盖所有给定的线段。这类似于经典的区间覆盖问题,其中我们需要选择尽可能少的区间来覆盖所有其他区间。

解题思路

  1. 排序处理:首先将所有线段按照起点升序排序,如果起点相同,则按照终点降序排序。这样做的目的是为了方便后续处理,确保我们总是先处理起点较小且覆盖范围较大的线段。
  2. 贪心选择:使用贪心策略来选择线段。初始化一个变量end表示当前已经覆盖的最远位置。遍历排序后的线段:
    • 如果当前线段的起点大于end,说明它不在已覆盖的范围内,必须选择这条线段,并更新end为这条线段的终点。
    • 如果当前线段的起点小于等于end,说明它与已覆盖的范围有重叠,此时选择能够覆盖更远终点的线段(即更新end为当前线段的终点和原end中的较大值)。
  3. 计数:每次选择一个新的线段时,增加计数器。

Java

import java.util.Arrays;
import java.util.Scanner;
/**
 * @author code5bug
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取线段数量
        int n = scanner.nextInt();
        scanner.nextLine(); // 读取n后消耗换行符

        // 使用二维数组存储线段,segments[i][0]表示起点,segments[i][1]表示终点
        int[][] segments = new int[n][2];
        for (int i = 0; i < n; i++) {
            String[] parts = scanner.nextLine().split(",");
            segments[i][0] = Integer.parseInt(parts[0]);
            segments[i][1] = Integer.parseInt(parts[1]);
        }

        // 对线段进行排序:先按起点升序,起点相同则按终点降序
        Arrays.sort(segments, (a, b) -> {
            if (a[0] != b[0]) {
                return Integer.compare(a[0], b[0]); // 起点升序
            } else {
                return Integer.compare(b[1], a[1]); // 终点降序
            }
        });

        int count = 0; // 记录最少需要的线段数量
        // end 当前已覆盖的最远位置Ï
        for (int i = 0, end = Integer.MIN_VALUE; i < n; ) {
            int prevEnd = end; // 记录之前的覆盖终点

            // 如果当前线段的起点超出已覆盖范围,必须选择该线段
            if (prevEnd < segments[i][0]) {
                end = segments[i++][1];
            } else {
                // 在重叠的线段中选择终点最大的线段
                while (i < n && segments[i][0] <= prevEnd) {
                    end = Math.max(end, segments[i++][1]);
                }
            }

            // 如果覆盖范围扩展了,增加计数器
            if (prevEnd < end) count++;
        }

        // 输出结果
        System.out.println(count);
    }
}

整理题解不易, 如果有帮助到您,请给点个赞 ‍❤️‍ 和收藏 ⭐,让更多的人看到。🙏🙏🙏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

什码情况

你的鼓励就是我最大的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值