华为OD机试题库《C++》限时优惠 9.9
华为OD机试题库《Python》限时优惠 9.9
华为OD机试题库《JavaScript》限时优惠 9.9
针对刷题难,效率慢,我们提供一对一算法辅导, 针对个人情况定制化的提高计划(全称1V1效率更高)。
看不懂有疑问需要答疑辅导欢迎私VX: code5bug
给定坐标轴上的一组线段,线段的起点和终点均为整数并且长度不小于1,请你从中找到最少数量的线段,这些线段可以覆盖住所有线段。
最少线段数量,为正整数
输入:
3
1,7
3,6
8,9
输出:
2
说明:
选取2条线段[1,7]和[8,9]即可,这两条线段可以覆盖[3,6]。
输入:
4
1,4
2,5
3,6
10,20
输出:
3
输入:
4
1,4
2,5
3,6
1,10
输出:
1
这道题目属于贪心算法和区间覆盖问题。我们需要找到最少数量的线段,使得这些线段能够覆盖所有给定的线段。这类似于经典的区间覆盖问题,其中我们需要选择尽可能少的区间来覆盖所有其他区间。
解题思路
- 排序处理:首先将所有线段按照起点升序排序,如果起点相同,则按照终点降序排序。这样做的目的是为了方便后续处理,确保我们总是先处理起点较小且覆盖范围较大的线段。
- 贪心选择:使用贪心策略来选择线段。初始化一个变量
end
表示当前已经覆盖的最远位置。遍历排序后的线段:
- 如果当前线段的起点大于
end
,说明它不在已覆盖的范围内,必须选择这条线段,并更新end
为这条线段的终点。- 如果当前线段的起点小于等于
end
,说明它与已覆盖的范围有重叠,此时选择能够覆盖更远终点的线段(即更新end
为当前线段的终点和原end
中的较大值)。- 计数:每次选择一个新的线段时,增加计数器。
const rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout,
});
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
// Author: code5bug
(async () => {
const n = parseInt(await readline());
const segs = [];
for (let i = 0; i < n; i++) {
let [a, b] = (await readline()).split(',').map(Number);
segs.push([a, b]);
}
// 根据线段起点升序,终点降序排序
segs.sort((a, b) => (a[0] != b[0] ? a[0] - b[0] : b[1] - a[1]));
let count = 0;
// end 表示当前已经覆盖的区域
for (let i = 0, end = -Infinity; i < n; ) {
const prev_end = end;
// 碰到一个不向交的新区间,则此区间必须选择
if (prev_end < segs[i][0]) {
end = segs[i++][1];
} else {
// 在系列向交的区间中选择可以使得覆盖区间跨越更远的一个区间
while (i < n && segs[i][0] <= prev_end)
end = Math.max(end, segs[i++][1]);
}
// 选择了一个end结尾的新的区间
if (prev_end < end) count++;
}
console.log(count);
rl.close();
})();
整理题解不易, 如果有帮助到您,请给点个赞 ❤️ 和收藏 ⭐,让更多的人看到。