华为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)。

在分配连接器时,采用贪心策略,即优先使用最小的连接器来桥接最小的间隙。这确保了较大的连接器可以保留用于桥接较大的间隙,从而最大化桥接的间隙数量,最终达到最小化区间数量的目标。

本题的目标是通过合并重叠或相邻的区间,并使用给定的连接器来连接分离的区间,以达到最小化最终区间数量的目的。具体步骤如下:

  1. 合并区间:
    • 首先对所有区间按照起点进行排序。
    • 遍历排序后的区间,合并所有重叠或相邻的区间,得到一个合并后的区间列表。
  2. 计算区间之间的间隙:
    • 在合并后的区间列表中,计算相邻两个区间之间的间隙(即当前区间的起点减去前一个区间的终点)。
    • 这些间隙代表了需要使用连接器来桥接的地方。
  3. 分配连接器:
    • 将所有间隙按照从小到大排序。
    • 将所有连接器按照从小到大排序。
    • 使用贪心算法,尝试使用最小的连接器来桥接最小的间隙,以最大化桥接的间隙数量。
    • 每桥接一个间隙,最终的区间数量就会减少一个。
  4. 输出结果:
    • 最终的区间数量等于合并后的区间数量减去成功桥接的间隙数量。

六、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在线答疑。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值