华为OD机试题库《C++》限时优惠 9.9
华为OD机试题库《Python》限时优惠 9.9
华为OD机试题库《JavaScript》限时优惠 9.9
针对刷题难,效率慢,我们提供一对一算法辅导, 针对个人情况定制化的提高计划(全称1V1效率更高)。
看不懂有疑问需要答疑辅导欢迎私VX: code5bug
题目描述
给定坐标轴上的一组线段,线段的起点和终点均为整数并且长度不小于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
题解
这道题目属于贪心算法和区间覆盖问题。我们需要找到最少数量的线段,使得这些线段能够覆盖所有给定的线段。这类似于经典的区间覆盖问题,其中我们需要选择尽可能少的区间来覆盖所有其他区间。
解题思路
- 排序处理:首先将所有线段按照起点升序排序,如果起点相同,则按照终点降序排序。这样做的目的是为了方便后续处理,确保我们总是先处理起点较小且覆盖范围较大的线段。
- 贪心选择:使用贪心策略来选择线段。初始化一个变量
end
表示当前已经覆盖的最远位置。遍历排序后的线段:
- 如果当前线段的起点大于
end
,说明它不在已覆盖的范围内,必须选择这条线段,并更新end
为这条线段的终点。- 如果当前线段的起点小于等于
end
,说明它与已覆盖的范围有重叠,此时选择能够覆盖更远终点的线段(即更新end
为当前线段的终点和原end
中的较大值)。- 计数:每次选择一个新的线段时,增加计数器。
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);
}
}
整理题解不易, 如果有帮助到您,请给点个赞 ❤️ 和收藏 ⭐,让更多的人看到。🙏🙏🙏