华为OD机试 - 几何平均值最大子数(Java 2024 E卷 200分)

在这里插入图片描述

华为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且几何平均值最大的子数组,并输出其起始位置和长度。如果存在多个满足条件的子数组,我们需要按照以下优先级选择:

  1. 几何平均值最大的子数组。
  2. 如果有多个几何平均值相同,选择长度最小的子数组。
  3. 如果仍有多个,选择最前面的子数组。

由于N的范围较大(1 ≤ N ≤ 100,000),需要设计一个高效的算法。

2、具体步骤:

  1. 读取输入:读取N和L,接着读取N个正数。
  2. 对数转换:对每个数取自然对数,存储在一个数组中。
  3. 前缀和计算:计算对数数组的前缀和,以便快速计算任意子数组的对数和。
  4. 滑动窗口遍历:遍历所有长度为L的子数组,计算其对数和,记录最大对数和及其起始位置。
  5. 输出结果:输出起始位置和子数组长度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在线答疑。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

哪 吒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值