华为OD机试 - 最小极差分组(Java/Python/JS/C/C++/Go 新系统 100分)

在这里插入图片描述

华为OD机试 新系统 统一考试题库清单(持续收录中)以及考点说明(Python/JS/C/C++)

专栏导读

本专栏收录于《华为OD机试真题(Java/Python/JS/C/C++/Go)》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

一、题目描述

给定 n 个整数,你需要将它们任意分成两组,且两组都不能为空。
对于一组数,它的极差定义为该组中的最大值减去最小值。
设两组分别为第 1 组和第 2 组,则它们的极差分别为:

• 第 1 组极差 = 第 1 组最大值 − 第 1 组最小值

• 第 2 组极差 = 第 2 组最大值 − 第 2 组最小值
你的目标是最小化这两个极差之和,即最小化:
(max1 − min1) + (max2 − min2)
请输出这个最小值。

二、测试用例

测试用例1:

1、输入

5
1 1 9 1 9

2、输出

0

3、说明

排序后:

1 1 1 9 9

最大相邻差为:

9 - 1 = 8

整体极差也是:

9 - 1 = 8

因此:

8 - 8 = 0

可以分成:

[1, 1, 1]
[9, 9]

两组极差都是 0。

测试用例2:

1、输入

5
10 1 5 3 8

2、输出

6

3、说明

排序后:

1 3 5 8 10

相邻差:

2 2 3 2

最大相邻差为 3。

整体极差:

10 - 1 = 9

所以:

9 - 3 = 6

可以划分为:

[1, 3, 5]
[8, 10]

极差分别为 4 和 2,总和为 6。

三、解题思路

先将 n 个整数从小到大排序,记为 a[0], a[1], ..., a[n-1]

两组中每组的贡献只与该组的最大值和最小值有关。考虑一组数对应区间 [min1, max1],另一组对应 [min2, max2]。如果两个区间发生交叉或包含,那么两个极差之和不会小于整个数组的极差 a[n-1] - a[0];而把排序后的数组从某两个相邻元素之间切开,得到的结果一定不超过整体极差。因此最优方案一定可以选择为:排序后前 k 个元素属于第一组,剩余元素属于第二组。

假设在 a[k]a[k+1] 之间切分,则:

第一组极差:
a[k] - a[0]

第二组极差:
a[n-1] - a[k+1]

两者之和为:

a[n-1] - a[0] - (a[k+1] - a[k])

其中 a[n-1] - a[0] 是固定值,因此要让结果最小,就要让切分位置的相邻差
a[k+1] - a[k] 尽可能大。

所以最终答案为:

整体极差 - 最大相邻差

四、Java算法源码

public class OdTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        if (!scanner.hasNextInt()) {
            return;
        }

        int n = scanner.nextInt();
        long[] nums = new long[n];

        for (int i = 0; i < n; i++) {
            nums[i] = scanner.nextLong();
        }

        // 题目要求分成两个非空组,因此正常情况下 n >= 2。
        // 这里增加保护逻辑,避免异常输入造成数组访问越界。
        if (n < 2) {
            System.out.println(0);
            return;
        }

        // 排序以后,最优方案一定可以在某两个相邻元素之间进行切分:
        // 左侧所有元素属于一组,右侧所有元素属于另一组。
        Arrays.sort(nums);

        long maxGap = 0;

        for (int i = 0; i < n - 1; i++) {
            /*
             * 如果在 nums[i] 和 nums[i + 1] 之间切分,
             * 那么这两个数之间的距离不会被计算到任意一组的极差中。
             *
             * 因此应该找到最大的相邻差,把它作为两组的分界线,
             * 从而最大程度降低两个极差之和。
             */
            long gap = nums[i + 1] - nums[i];
            maxGap = Math.max(maxGap, gap);
        }

        /*
         * 假设整体极差为:
         * nums[n - 1] - nums[0]
         *
         * 切分之后,相当于从整体极差中去掉分界位置的相邻差,
         * 所以最优答案就是:
         * 整体极差 - 最大相邻差。
         */
        long answer = nums[n - 1] - nums[0] - maxGap;

        System.out.println(answer);
    }
}

五、效果展示

1、输入

6
1 2 3 100 101 102

2、输出

4

3、说明

最大相邻差:

100 - 3 = 97

整体极差:

102 - 1 = 101

答案:

101 - 97 = 4

最佳分组:

[1, 2, 3]
[100, 101, 102]

两个极差分别为 2、2。

在这里插入图片描述

六、Python算法源码

import sys


def main():
    data = sys.stdin.read().split()
    if not data:
        return

    n = int(data[0])
    nums = list(map(int, data[1:1 + n]))

    # 题目要求两组都不能为空,因此正常情况下 n >= 2。
    # 这里增加保护处理,避免异常输入。
    if n < 2:
        print(0)
        return

    # 排序之后,最优划分可以看成在某两个相邻元素之间切分。
    nums.sort()

    # 分界处的相邻差不会计入任何一组内部的极差。
    # 因此,应当选择最大的相邻差作为分界位置。
    max_gap = max(
        nums[i + 1] - nums[i]
        for i in range(n - 1)
    )

    # 最小极差和 = 整体极差 - 最大相邻差
    answer = nums[-1] - nums[0] - max_gap

    print(answer)


if __name__ == "__main__":
    main()

七、JavaScript算法源码

const fs = require('fs');

const input = fs.readFileSync(0, 'utf8').trim();

if (!input) {
    process.exit(0);
}

const tokens = input.split(/\s+/);
const n = Number(tokens[0]);

const nums = [];

for (let i = 0; i < n; i++) {
    // 使用 BigInt 保存整数,避免 Number 超过安全整数范围后产生精度问题。
    nums.push(BigInt(tokens[i + 1]));
}

if (n < 2) {
    console.log('0');
    process.exit(0);
}

/*
 * BigInt 不能通过 (a, b) => a - b 直接作为 sort 比较器,
 * 因为比较器要求返回 Number。
 * 因此通过大小关系返回 -1、0、1。
 */
nums.sort((a, b) => {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
});

let maxGap = 0n;

for (let i = 0; i < n - 1; i++) {
    /*
     * 如果在 nums[i] 和 nums[i + 1] 之间切分,
     * 这一段距离不会计入任何一组的极差。
     * 因此寻找最大的相邻差作为分界。
     */
    const gap = nums[i + 1] - nums[i];

    if (gap > maxGap) {
        maxGap = gap;
    }
}

// 最小极差和 = 整体极差 - 最大相邻差
const answer = nums[n - 1] - nums[0] - maxGap;

console.log(answer.toString());

八、C算法源码

#include <stdio.h>
#include <stdlib.h>

/*
 * qsort 的比较函数。
 * 不直接返回 x - y,因为 long long 的差值转换成 int 时可能溢出。
 */
static int cmpLongLong(const void *a, const void *b) {
    long long x = *(const long long *)a;
    long long y = *(const long long *)b;

    return (x > y) - (x < y);
}

int main(void) {
    int n;

    if (scanf("%d", &n) != 1) {
        return 0;
    }

    long long *nums =
        (long long *)malloc(sizeof(long long) * (size_t)n);

    if (nums == NULL && n > 0) {
        return 0;
    }

    for (int i = 0; i < n; ++i) {
        scanf("%lld", &nums[i]);
    }

    // 题目正常输入满足 n >= 2,这里进行异常输入保护。
    if (n < 2) {
        printf("0\n");
        free(nums);
        return 0;
    }

    /*
     * 排序后,只需要寻找最大的相邻差。
     * 最大相邻差所在位置就是最优的两组分界位置。
     */
    qsort(nums, (size_t)n, sizeof(long long), cmpLongLong);

    long long maxGap = 0;

    for (int i = 0; i < n - 1; ++i) {
        long long gap = nums[i + 1] - nums[i];

        if (gap > maxGap) {
            maxGap = gap;
        }
    }

    // 最小极差和 = 整体极差 - 最大相邻差
    long long answer =
        nums[n - 1] - nums[0] - maxGap;

    printf("%lld\n", answer);

    free(nums);
    return 0;
}

九、C++算法源码

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;

    if (!(cin >> n)) {
        return 0;
    }

    vector<long long> nums(n);

    for (auto &x : nums) {
        cin >> x;
    }

    // 正常数据满足 n >= 2,这里保留保护逻辑。
    if (n < 2) {
        cout << 0 << '\n';
        return 0;
    }

    /*
     * 排序后,最优划分一定可以在两个相邻元素之间完成。
     * 因此只需要寻找最大的相邻差。
     */
    sort(nums.begin(), nums.end());

    long long maxGap = 0;

    for (int i = 0; i < n - 1; ++i) {
        /*
         * 最大相邻差作为分界线时,
         * 可以从整体极差中“去掉”最大的那一段距离。
         */
        maxGap = max(
            maxGap,
            nums[i + 1] - nums[i]
        );
    }

    // 最小极差和 = 整体极差 - 最大相邻差
    long long answer =
        nums.back() - nums.front() - maxGap;

    cout << answer << '\n';

    return 0;
}

十、Go算法源码

package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
)

func main() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var n int

	if _, err := fmt.Fscan(in, &n); err != nil {
		return
	}

	nums := make([]int64, n)

	for i := 0; i < n; i++ {
		fmt.Fscan(in, &nums[i])
	}

	// 题目正常输入满足 n >= 2,这里进行保护处理。
	if n < 2 {
		fmt.Fprintln(out, 0)
		return
	}

	/*
		排序后,最优方案可以看成:
		在某两个相邻元素之间切分,
		左边构成一组,右边构成另一组。
	*/
	sort.Slice(nums, func(i, j int) bool {
		return nums[i] < nums[j]
	})

	var maxGap int64 = 0

	for i := 0; i < n-1; i++ {
		/*
			分界位置的相邻距离不会计入任意一组的极差,
			因此找到最大的相邻差作为分界即可。
		*/
		gap := nums[i+1] - nums[i]

		if gap > maxGap {
			maxGap = gap
		}
	}

	// 最小极差和 = 整体极差 - 最大相邻差
	answer := nums[n-1] - nums[0] - maxGap

	fmt.Fprintln(out, answer)
}


🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 新系统 200分)

🏆本文收录于,华为OD机试真题(Java/Python/JS/C/C++/Go)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

哪 吒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值