华为OD机试 - 连接器问题 - 二分查找(Python/JS/C/C++ 2024 E卷 200分)
华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
有一组区间 [a0, b0], [a1, b1], …, (a, b 表示起点、终点),区间有可能重叠、相邻,重叠或相邻则可以合并为更大的区间;
给定一组连接器 [x1, x2, x3, …](x 表示连接器的最大可连接长度,即 x >= gap),可用于将分离的区间连接起来,但两个分离区间之间只能使用 1 个连接器;
请编程实现使用连接器后,最少的区间数结果。
区间数量 < 10000, a, b 均 <= 10000
连接器数量 < 10000, x <= 10000
二、输入描述
区间组:[1,10], [15,20], [18,30], [33,40]
连接器组:[5,4,3,2]
三、输出描述
1
说明
合并后 [1,10] [15,20] [18,30] [33,40],使用 5,3 两个连接器连接后只剩下 [1,40]。
四、测试用例
测试用例1:
1、输入
[1,10], [15,20], [18,30], [33,40]
[5,4,3,2]
2、输出
1
3、说明
合并后:[1,10], [15,30], [33,40],使用5,3两个连接器连接后只剩下[1,40]。
测试用例2:
1、输入
[1,2], [3,5], [7,10], [15,20], [30,100]
[5,4,3,2,1]
2、输出
2
3、说明
无重叠和相邻,使用1,2,5三个连接器连接后只剩下[1,20],[30,100]。
五、解题思路
首先对区间和连接器进行排序,便于后续的合并和分配操作。排序的时间复杂度为O(N log N),适用于区间和连接器数量较大的情况(N < 10000)。
在分配连接器时,采用贪心策略,即优先使用最小的连接器来桥接最小的间隙。这确保了较大的连接器可以保留用于桥接较大的间隙,从而最大化桥接的间隙数量,最终达到最小化区间数量的目标。
本题的目标是通过合并重叠或相邻的区间,并使用给定的连接器来连接分离的区间,以达到最小化最终区间数量的目的。具体步骤如下:
- 合并区间:
- 首先对所有区间按照起点进行排序。
- 遍历排序后的区间,合并所有重叠或相邻的区间,得到一个合并后的区间列表。
- 计算区间之间的间隙:
- 在合并后的区间列表中,计算相邻两个区间之间的间隙(即当前区间的起点减去前一个区间的终点)。
- 这些间隙代表了需要使用连接器来桥接的地方。
- 分配连接器:
- 将所有间隙按照从小到大排序。
- 将所有连接器按照从小到大排序。
- 使用贪心算法,尝试使用最小的连接器来桥接最小的间隙,以最大化桥接的间隙数量。
- 每桥接一个间隙,最终的区间数量就会减少一个。
- 输出结果:
- 最终的区间数量等于合并后的区间数量减去成功桥接的间隙数量。
六、Python算法源码
import sys
import re
import bisect
# 定义区间类,包含起点和终点
class Interval:
def __init__(self, start, end):
self.start = start # 起点
self.end = end # 终点
# 定义排序规则,按起点排序
def __lt__(self, other):
if self.start != other.start:
return self.start < other.start
return self.end < other.end
def parse_intervals(input_str):
intervals = []
# 使用正则表达式匹配[数,数]的格式
pattern = re.compile(r'\[(\d+),(\d+)\]')
matches = pattern.findall(input_str)
for match in matches:
start = int(match[0]) # 提取起点
end = int(match[1]) # 提取终点
intervals.append(Interval(start, end)) # 添加到区间列表
return intervals
def parse_connectors(input_str):
connectors = []
# 使用正则表达式匹配所有数字
pattern = re.compile(r'\d+')
matches = pattern.findall(input_str)
for match in matches:
connectors.append(int(match)) # 添加连接器到列表
return connectors
def merge_intervals(intervals):
merged = []
prev = intervals[0] # 初始化前一个区间为第一个区间
for current in intervals[1:]:
# 如果当前区间的起点 <= 前一个区间的终点 +1,进行合并
if current.start <= prev.end + 1:
prev.end = max(prev.end, current.end) # 更新前一个区间的终点
else:
merged.append(prev) # 添加前一个区间到合并列表
prev = current # 更新前一个区间为当前区间
merged.append(prev) # 添加最后一个区间
return merged
def main():
# 读取标准输入的所有行
input_lines = sys.stdin.read().splitlines()
if len(input_lines) < 2:
print(0) # 如果输入不足两行,输出0
return
interval_input = input_lines[0].strip() # 第一行是区间输入
connector_input = input_lines[1].strip() # 第二行是连接器输入
intervals = parse_intervals(interval_input) # 解析区间
connectors = parse_connectors(connector_input) # 解析连接器
if not intervals:
print(0) # 如果没有区间,输出0
return
intervals.sort() # 按起点排序
merged = merge_intervals(intervals) # 合并重叠或相邻的区间
if len(merged) == 1:
print(1) # 如果合并后只有一个区间,输出1
return
gaps = []
for i in range(1, len(merged)):
gap = merged[i].start - merged[i-1].end # 计算间隙
gaps.append(gap) # 添加到间隙列表
gaps.sort() # 按间隙从小到大排序
connectors.sort() # 按连接器从小到大排序
bridged_gaps = 0 # 初始化桥接的间隙数量
connector_index = 0 # 初始化连接器的索引
for gap in gaps:
# 使用二分查找找到第一个 >= gap 的连接器索引
idx = bisect.bisect_left(connectors, gap, connector_index)
if idx < len(connectors):
bridged_gaps += 1 # 成功桥接一个间隙
connector_index = idx + 1 # 更新连接器索引,避免重复使用
else:
break # 如果没有足够的连接器,退出循环
# 最终的区间数量等于合并后的区间数量减去桥接的间隙数量
print(len(merged) - bridged_gaps)
if __name__ == "__main__":
main()
七、JavaScript算法源码
// 引入必要的模块
const readline = require('readline');
// 创建接口以读取标准输入
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
// 定义区间类,包含起点和终点
class Interval {
constructor(start, end) {
this.start = start; // 起点
this.end = end; // 终点
}
}
// 解析区间输入字符串,返回区间列表
function parseIntervals(input) {
const intervals = [];
const pattern = /\[(\d+),(\d+)\]/g; // 正则表达式匹配[数,数]
let match;
while ((match = pattern.exec(input)) !== null) {
const start = parseInt(match[1], 10); // 提取起点
const end = parseInt(match[2], 10); // 提取终点
intervals.push(new Interval(start, end)); // 添加到区间列表
}
return intervals;
}
// 解析连接器输入字符串,返回连接器列表
function parseConnectors(input) {
const connectors = [];
const pattern = /\d+/g; // 正则表达式匹配所有数字
let match;
while ((match = pattern.exec(input)) !== null) {
connectors.push(parseInt(match[0], 10)); // 添加连接器到列表
}
return connectors;
}
// 合并重叠或相邻的区间
function mergeIntervals(intervals) {
const merged = [];
let prev = intervals[0]; // 初始化前一个区间为第一个区间
for (let i = 1; i < intervals.length; i++) {
const current = intervals[i];
// 如果当前区间的起点 <= 前一个区间的终点 +1,进行合并
if (current.start <= prev.end + 1) {
prev.end = Math.max(prev.end, current.end); // 更新前一个区间的终点
} else {
merged.push(prev); // 添加前一个区间到合并列表
prev = current; // 更新前一个区间为当前区间
}
}
merged.push(prev); // 添加最后一个区间
return merged;
}
// 主函数
rl.on('line', function(line) {
// 读取第一行区间输入
const intervalInput = line.trim();
// 读取第二行连接器输入
rl.once('line', function(connectorLine) {
const connectorInput = connectorLine.trim();
const intervals = parseIntervals(intervalInput); // 解析区间
const connectors = parseConnectors(connectorInput); // 解析连接器
if (intervals.length === 0) {
console.log(0); // 如果没有区间,输出0
process.exit(0);
}
// 按起点排序
intervals.sort((a, b) => a.start - b.start || a.end - b.end);
const merged = mergeIntervals(intervals); // 合并重叠或相邻的区间
if (merged.length === 1) {
console.log(1); // 如果合并后只有一个区间,输出1
process.exit(0);
}
const gaps = [];
for (let i = 1; i < merged.length; i++) {
const gap = merged[i].start - merged[i-1].end; // 计算间隙
gaps.push(gap); // 添加到间隙列表
}
gaps.sort((a, b) => a - b); // 按间隙从小到大排序
connectors.sort((a, b) => a - b); // 按连接器从小到大排序
let bridgedGaps = 0; // 初始化桥接的间隙数量
let connectorIndex = 0; // 初始化连接器的索引
for (const gap of gaps) {
// 使用二分查找找到第一个 >= gap 的连接器索引
let left = connectorIndex;
let right = connectors.length;
let idx = connectors.length;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (connectors[mid] >= gap) {
idx = mid;
right = mid;
} else {
left = mid + 1;
}
}
if (idx < connectors.length) {
bridgedGaps += 1; // 成功桥接一个间隙
connectorIndex = idx + 1; // 更新连接器索引,避免重复使用
} else {
break; // 如果没有足够的连接器,退出循环
}
}
// 最终的区间数量等于合并后的区间数量减去桥接的间隙数量
console.log(merged.length - bridgedGaps);
process.exit(0);
});
});
八、C算法源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// 定义区间结构体,包含起点和终点
typedef struct {
int start; // 起点
int end; // 终点
} Interval;
// 比较函数,用于qsort按起点排序
int compareIntervals(const void* a, const void* b) {
Interval* ia = (Interval*)a;
Interval* ib = (Interval*)b;
if (ia->start != ib->start)
return ia->start - ib->start;
return ia->end - ib->end;
}
// 主函数
int main() {
char intervalInput[100000]; // 存储区间输入
char connectorInput[100000]; // 存储连接器输入
// 读取第一行区间输入
if (fgets(intervalInput, sizeof(intervalInput), stdin) == NULL) {
printf("0\n"); // 如果读取失败,输出0
return 0;
}
// 读取第二行连接器输入
if (fgets(connectorInput, sizeof(connectorInput), stdin) == NULL) {
printf("0\n"); // 如果读取失败,输出0
return 0;
}
// 解析区间
Interval intervals[10000];
int intervalCount = 0;
char* ptr = intervalInput;
while (*ptr) {
if (*ptr == '[') {
ptr++;
int start = 0, end = 0;
// 读取起点
while (isdigit(*ptr)) {
start = start * 10 + (*ptr - '0');
ptr++;
}
if (*ptr == ',') ptr++;
// 读取终点
while (isdigit(*ptr)) {
end = end * 10 + (*ptr - '0');
ptr++;
}
if (*ptr == ']') ptr++;
intervals[intervalCount].start = start; // 设置起点
intervals[intervalCount].end = end; // 设置终点
intervalCount++;
} else {
ptr++;
}
}
// 解析连接器
int connectors[10000];
int connectorCount = 0;
ptr = connectorInput;
while (*ptr) {
if (isdigit(*ptr)) {
int num = 0;
while (isdigit(*ptr)) {
num = num * 10 + (*ptr - '0');
ptr++;
}
connectors[connectorCount++] = num; // 添加连接器
} else {
ptr++;
}
}
if (intervalCount == 0) {
printf("0\n"); // 如果没有区间,输出0
return 0;
}
// 按起点排序
qsort(intervals, intervalCount, sizeof(Interval), compareIntervals);
// 合并重叠或相邻的区间
Interval merged[10000];
int mergedCount = 0;
merged[mergedCount] = intervals[0]; // 初始化第一个合并区间
mergedCount++;
for (int i = 1; i < intervalCount; i++) {
Interval current = intervals[i];
Interval* last = &merged[mergedCount - 1];
if (current.start <= last->end + 1) {
// 合并区间
if (current.end > last->end) {
last->end = current.end;
}
} else {
merged[mergedCount++] = current; // 添加新的合并区间
}
}
if (mergedCount == 1) {
printf("1\n"); // 如果合并后只有一个区间,输出1
return 0;
}
// 计算所有间隙
int gaps[10000];
int gapCount = 0;
for (int i = 1; i < mergedCount; i++) {
gaps[gapCount++] = merged[i].start - merged[i-1].end;
}
// 按间隙从小到大排序
qsort(gaps, gapCount, sizeof(int), compareIntervals);
// 按连接器从小到大排序
qsort(connectors, connectorCount, sizeof(int), compareIntervals);
// 使用贪心算法桥接尽可能多的间隙
int bridgedGaps = 0;
int connectorIndex = 0;
for (int i = 0; i < gapCount; i++) {
int gap = gaps[i];
// 使用二分查找找到第一个 >= gap 的连接器
int left = connectorIndex;
int right = connectorCount;
int idx = connectorCount;
while (left < right) {
int mid = left + (right - left) / 2;
if (connectors[mid] >= gap) {
idx = mid;
right = mid;
} else {
left = mid + 1;
}
}
if (idx < connectorCount) {
bridgedGaps++; // 成功桥接一个间隙
connectorIndex = idx + 1; // 更新连接器索引,避免重复使用
} else {
break; // 如果没有足够的连接器,退出循环
}
}
// 最终的区间数量等于合并后的区间数量减去桥接的间隙数量
printf("%d\n", mergedCount - bridgedGaps);
return 0;
}
九、C++算法源码
#include <bits/stdc++.h>
using namespace std;
// 定义区间结构体,包含起点和终点
struct Interval {
int start; // 起点
int end; // 终点
// 重载小于运算符,用于排序
bool operator<(const Interval& other) const {
if (start != other.start)
return start < other.start;
return end < other.end;
}
};
// 主函数
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
string intervalInput;
string connectorInput;
// 读取第一行区间输入
getline(cin, intervalInput);
// 读取第二行连接器输入
getline(cin, connectorInput);
// 解析区间
vector<Interval> intervals;
regex pattern("\\[(\\d+),(\\d+)\\]");
auto words_begin = sregex_iterator(intervalInput.begin(), intervalInput.end(), pattern);
auto words_end = sregex_iterator();
for(auto it = words_begin; it != words_end; ++it){
smatch match = *it;
Interval iv;
iv.start = stoi(match[1]);
iv.end = stoi(match[2]);
intervals.push_back(iv);
}
// 解析连接器
vector<int> connectors;
regex num_pattern("(\\d+)");
auto num_begin = sregex_iterator(connectorInput.begin(), connectorInput.end(), num_pattern);
auto num_end = sregex_iterator();
for(auto it = num_begin; it != num_end; ++it){
smatch match = *it;
connectors.push_back(stoi(match[1]));
}
if(intervals.empty()){
cout << "0\n"; // 如果没有区间,输出0
return 0;
}
// 按起点排序
sort(intervals.begin(), intervals.end());
// 合并重叠或相邻的区间
vector<Interval> merged;
merged.push_back(intervals[0]); // 初始化第一个合并区间
for(int i=1;i<intervals.size();i++){
Interval current = intervals[i];
Interval& last = merged.back();
if(current.start <= last.end +1){
last.end = max(last.end, current.end); // 更新前一个区间的终点
}
else{
merged.push_back(current); // 添加新的合并区间
}
}
if(merged.size() ==1){
cout << "1\n"; // 如果合并后只有一个区间,输出1
return 0;
}
// 计算所有间隙
vector<int> gaps;
for(int i=1;i<merged.size();i++){
int gap = merged[i].start - merged[i-1].end;
gaps.push_back(gap);
}
// 按间隙从小到大排序
sort(gaps.begin(), gaps.end());
// 按连接器从小到大排序
sort(connectors.begin(), connectors.end());
// 使用贪心算法桥接尽可能多的间隙
int bridgedGaps =0;
int connectorIndex =0;
for(auto gap : gaps){
// 使用二分查找找到第一个 >= gap 的连接器索引
int idx = lower_bound(connectors.begin() + connectorIndex, connectors.end(), gap) - connectors.begin();
if(idx < connectors.size()){
bridgedGaps++; // 成功桥接一个间隙
connectorIndex = idx +1; // 更新连接器索引,避免重复使用
}
else{
break; // 如果没有足够的连接器,退出循环
}
}
// 最终的区间数量等于合并后的区间数量减去桥接的间隙数量
cout << merged.size() - bridgedGaps << "\n";
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2024 E卷 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
原文地址:https://blog.csdn.net/guorui_java/article/details/142697605
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!