
专栏导读
本专栏收录于《华为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算法的适用场景,发现新题目,随时更新。

45万+

被折叠的 条评论
为什么被折叠?



