2023华为OD机试真题【区间交叠/贪心算法】【Python Java C++】

题目描述

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

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

最少线段数量,为正整数。
输入
3
1,4
2,5
3,6
输出
2

题意解读

首先,用示例来理解题意:现在有三条线段:

一号线段:起点1,终点4;

二号线段:起点2,终点5;

三号线段:起点3,终点6;

2023华为OD机试真题【区间交叠/贪心算法】【Python Java C++】_第1张图片
我们要从这三条线段中,选出若干条线段,覆盖1~6整个区间。

比如,我们可以选择 一号、二号、三号。一号覆盖 1~4 ,二号覆盖 2~5,三号覆盖3~6,三条线段加起来可以覆盖1~6整个区间。但是,题目要求尽可能选择少的线段。因此,我们只用选择一号、三号,也能覆盖1~6整个区间。所以,答案就是选择2条线段(即一号、三号)。

解题思路

这是一道典型的贪心算法,贪心策略如下:

首先,将所有线段按起点从小到大排序。

然后遍历排序后的线段,每遍历到一个线段(我们称当前正在遍历的线段为current线段),找出后面的线段中左端点小于等于current线段的右端点的所有线段(我们称之为备选线段),找出备选线段中右端点最大的一个线段maxLine。下一步遍历maxLine

不断重复以上操作,直到覆盖完整个长度为m的区间,就能得到最少的线段数。

视频讲解

2023华为机试真题【区间交叠/贪心算法】

示例代码(Java版本)

import java.util.Scanner;
import java.util.*;
import java.util.stream.Collectors;

class Main {
    public static int minNum;

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        int count = Integer.parseInt(in.nextLine());
      
        // 所有的线段
        Integer[][] segments = new Integer[count][];
        for (int i = 0; i < count; i++) {
            segments[i] = Arrays.stream(in.nextLine().split(",")).map(Integer::parseInt).toArray(Integer[]::new);
        }

        // 按线段起点排序
        Arrays.sort(segments, (a, b) -> a[0] - b[0]);

        // 选择的线段
        LinkedList<Integer[]> selectedSegments = new LinkedList<>();
        selectedSegments.add(segments[0]);

        // 遍历所有线段
        for (int i = 1; i < segments.length; i++) {
            Integer[] segment = segments[i];

            while (true) {
                if (selectedSegments.size() == 0) {
                    selectedSegments.add(segment);
                    break;
                }

                int start1 = selectedSegments.get(selectedSegments.size() - 1)[0];
                int end1 = selectedSegments.get(selectedSegments.size() - 1)[1];
                int start2 = segment[0];
                int end2 = segment[1];

                if (start2 <= start1) {
                    if (end2 <= start1) {
                        break;
                    } else if (end2 < end1) {
                        break;
                    } else {
                        selectedSegments.removeLast();
                    }
                } else if (start2 < end1) {
                    if (end2 <= end1) {
                        break;
                    } else {
                        selectedSegments.add(new Integer[]{end1, end2});
                        break;
                    }
                } else {
                    selectedSegments.add(segment);
                    break;
                }
            }
        }

        // 最少线段数量
        System.out.println(selectedSegments.size());
    }
}


示例代码(Python版本)

# 输入
num_segments = int(input())
segments = []
for i in range(num_segments):
    segments.append([int(x) for x in input().split(",")])

# 按照线段的起点排序
segments.sort(key=lambda x: (x[0], -x[1]))

# 选定的线段栈
selected_segments = [segments[0]]

# 遍历所有线段
for segment in segments:
    while True:
        if not selected_segments:
            selected_segments.append(segment)
            break

        start_1, end_1 = selected_segments[-1]
        start_2, end_2 = segment

        if start_2 <= start_1:
            if end_2 <= start_1:
                break
            elif end_2 < end_1:
                break
            else:
                selected_segments.pop()
        elif start_2 < end_1:
            if end_2 <= end_1:
                break
            else:
                selected_segments.append([end_1, end_2])
                break
        else:
            selected_segments.append(segment)
            break

print(len(selected_segments))


示例代码(C++版本)

#include 
#include 
#include 
#include 
using namespace std;

bool compareSegments(pair<int, int> x, pair<int, int> y) {
    return x.first < y.first;
}

pair<int, int> parseSegment(const string &input_str) {
    pair<int, int> segment;
    int found = input_str.find(",");
    segment.first = stoi(input_str.substr(0, found));
    segment.second = stoi(input_str.substr(found + 1));
    return segment;
}

int main() {
    // 输入处理
    int n;
    cin >> n;
    vector<pair<int, int>> segments;
    for (int i = 0; i < n; i++) {
        string input_str;
        cin >> input_str;
        segments.push_back(parseSegment(input_str));
    }
    sort(segments.begin(), segments.end(), compareSegments);

    vector<pair<int, int>> covered_segments;
    covered_segments.push_back(segments[0]);

    for (int i = 1; i < segments.size(); i++) {
        pair<int, int> segment = segments[i];

        while (true) {
            if (covered_segments.size() == 0) {
                covered_segments.push_back(segment);
                break;
            }

            int start_1 = covered_segments.back().first;
            int end_1 = covered_segments.back().second;
            int start_2 = segment.first;
            int end_2 = segment.second;

            if (start_2 <= start_1) {
                if (end_2 <= start_1) {
                    break;
                } else if (end_2 < end_1) {
                    break;
                } else {
                    covered_segments.pop_back();
                }
            } else if (start_2 < end_1) {
                if (end_2 <= end_1) {
                    break;
                } else {
                    covered_segments.push_back(make_pair(end_1, end_2));
                    break;
                }
            } else {
                covered_segments.push_back(segment);
                break;
            }
        }
    }

    cout << covered_segments.size();
    return 0;
}

你可能感兴趣的:(贪心算法,算法,java,华为,面试)