华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
从一个长度为N的正整数数组numbers中找出长度至少为L且几何平均值Q最大的子数组,并输出其位置和大小。(K个数的几何平均值为K个数的乘积的K次方根)
若有多个子数组的几何平均值均为最大值Q,则输出长度最小的子数组。
若有多个长度最小的子数组的几何平均值均为最大值,则输出最前面的子数组。
二、输入描述
第1行输入N、L:
N表示numbers的大小(1 <= N <= 100000)
L表示子数组的最小长度(1 <= L <= N)
之后N行表示numbers中的N个数,每行一个(1 <= numbers[i] <= 10^9)
三、输出描述
输出子数组的位置(从0开始计数)和大小,中间用一个空格隔开。
备注
用例保证除几何平均值为最大值的子数组外,其他子数组的几何平均值至少比最大值小10^-10倍。
四、测试用例
测试用例1:
1、输入
3 2
2
2
3
2、输出
1 2
3、说明
长度至少为2的子数组共有三个,分别是{2,2}, {2,3}, {2,3},其中{2,3}的几何平均值最大,故输出其位置1和长度2。
测试用例2:
1、输入
10 2
0.2
0.2
0.1
0.2
0.2
0.2
0.1
0.2
0.2
0.2
2、输出
0 2
3、说明
所有长度为2的子数组及其几何平均值:
索引0-1: [0.2, 0.2],GM = 0.2
索引1-2: [0.2, 0.1],GM ≈ 0.141
索引2-3: [0.1, 0.2],GM ≈ 0.141
索引3-4: [0.2, 0.2],GM = 0.2
索引4-5: [0.2, 0.2],GM = 0.2
索引5-6: [0.2, 0.1],GM ≈ 0.141
索引6-7: [0.1, 0.2],GM ≈ 0.141
索引7-8: [0.2, 0.2],GM = 0.2
索引8-9: [0.2, 0.2],GM = 0.2
最大几何平均值为0.2,对应多个子数组。选择最早的子数组,即从索引0开始的子数组,因此输出0 2。
五、解题思路
1、问题分析
我们需要在一个长度为N的正数数组numbers中找出长度至少为L且几何平均值最大的子数组,并输出其起始位置和长度。如果存在多个满足条件的子数组,我们需要按照以下优先级选择:
- 几何平均值最大的子数组。
- 如果有多个几何平均值相同,选择长度最小的子数组。
- 如果仍有多个,选择最前面的子数组。
由于N的范围较大(1 ≤ N ≤ 100,000),需要设计一个高效的算法。
2、具体步骤:
- 读取输入:读取N和L,接着读取N个正数。
- 对数转换:对每个数取自然对数,存储在一个数组中。
- 前缀和计算:计算对数数组的前缀和,以便快速计算任意子数组的对数和。
- 滑动窗口遍历:遍历所有长度为L的子数组,计算其对数和,记录最大对数和及其起始位置。
- 输出结果:输出起始位置和子数组长度L。
六、Java算法源码
public class OdTest01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取数组大小N和子数组最小长度L
int N = scanner.nextInt();
int L = scanner.nextInt();
// 初始化用于存储数组元素的列表
List<Double> numbers = new ArrayList<>();
// 初始化最小值和最大值
double lowerBound = Double.MAX_VALUE; // 数组中的最小值
double upperBound = Double.MIN_VALUE; // 数组中的最大值
// 初始化前L个数的乘积
double currentProduct = 1.0;
// 读取数组元素并更新最小值、最大值及前L个数的乘积
for (int i = 0; i < N; i++) {
double num = scanner.nextDouble();
numbers.add(num);
lowerBound = Math.min(lowerBound, num); // 更新最小值
upperBound = Math.max(upperBound, num); // 更新最大值
if (i < L) {
currentProduct *= num; // 计算前L个数的乘积
}
}
// 初始化子数组的起始位置和长度
int subArrayStartIndex = 0; // 最大几何平均值子数组的起始位置
int subArrayLength = L; // 最大几何平均值子数组的长度,初始为L
// 使用二分法查找最大几何平均值
while (upperBound - lowerBound >= 1e-10) { // 精度设为1e-10
double midValue = (lowerBound + upperBound) / 2.0; // 计算中间值
double windowProduct = currentProduct; // 当前窗口的乘积
boolean found = false; // 是否找到满足条件的子数组的标志位
// 遍历所有长度为L的子数组,检查是否存在几何平均值 >= midValue
for (int i = L; i <= N; i++) {
// 如果当前窗口的几何平均值 >= midValue
if (windowProduct >= Math.pow(midValue, L)) {
subArrayStartIndex = i - L; // 更新子数组的起始位置
subArrayLength = L; // 更新子数组的长度
found = true; // 标记找到满足条件的子数组
break; // 退出循环
}
// 如果已到达数组末尾,退出循环
if (i == N) {
break;
}
// 更新窗口的乘积:除去最左边的元素,加入新的元素
windowProduct /= numbers.get(i - L);
windowProduct *= numbers.get(i);
}
// 根据是否找到满足条件的子数组,调整二分查找的边界
if (found) {
lowerBound = midValue; // 如果找到,调整下界
} else {
upperBound = midValue; // 如果未找到,调整上界
}
}
// 输出最大几何平均值子数组的起始位置和长度
System.out.println(subArrayStartIndex + " " + subArrayLength);
}
}
七、效果展示
1、输入
5 3
1
2
3
4
5
2、输出
2 3
3、说明
长度为3的子数组有:
索引0-2: [1, 2, 3],GM ≈ 1.817
索引1-3: [2, 3, 4],GM ≈ 2.884
索引2-4: [3, 4, 5],GM ≈ 3.915
最大几何平均值≈3.915,对应子数组从索引2开始,长度为3,因此输出2 3。
🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 2024 E卷 200分)
🏆本文收录于,华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。