华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中,刷题++点这里++

专栏导读

本专栏收录于《华为OD机试真题(Python/JS/C/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。

六、Python算法源码

python 复制代码
import math
import sys

def main():
    # 读取输入
    input = sys.stdin.read().split()
    index = 0
    
    # 读取数组大小N和子数组最小长度L
    N = int(input[index])
    index += 1
    L = int(input[index])
    index += 1
    
    # 初始化数组并计算对数值
    log_values = []
    for _ in range(N):
        num = float(input[index])
        index += 1
        log_values.append(math.log(num))  # 计算每个数的自然对数
    
    # 计算前缀和数组
    prefix_sums = [0.0] * (N + 1)
    for i in range(1, N + 1):
        prefix_sums[i] = prefix_sums[i - 1] + log_values[i - 1]
    
    # 初始化最大对数平均值及对应子数组的起始位置和长度
    max_log_average = -math.inf
    result_start_index = 0
    result_length = L
    
    # 初始化最小前缀和及其索引
    min_prefix = 0.0
    min_prefix_index = 0
    
    # 遍历前缀和数组,寻找最大对数平均值的子数组
    for i in range(L, N + 1):
        # 更新最小前缀和为[0, i - L]区间的最小前缀和
        if prefix_sums[i - L] < min_prefix:
            min_prefix = prefix_sums[i - L]
            min_prefix_index = i - L
        
        # 计算当前对数平均值
        current_log_average = (prefix_sums[i] - min_prefix) / L
        
        # 更新最大对数平均值及对应的子数组位置和长度
        if current_log_average > max_log_average:
            max_log_average = current_log_average
            result_start_index = min_prefix_index
            result_length = L
        elif math.isclose(current_log_average, max_log_average, abs_tol=1e-10):
            # 如果对数平均值相同,选择长度更小的子数组
            if L < result_length:
                result_start_index = min_prefix_index
                result_length = L
            # 如果长度相同,选择起始位置更靠前的子数组(无需操作,因为遍历顺序已保证)
    
    # 输出结果
    print(result_start_index, result_length)

if __name__ == "__main__":
    main()

七、JavaScript算法源码

javascript 复制代码
// 引入readline模块以读取输入
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let input = [];
rl.on('line', function(line){
    input = input.concat(line.trim().split(' '));
}).on('close', function(){
    let index = 0;
    
    // 读取数组大小N和子数组最小长度L
    const N = parseInt(input[index++]);
    const L = parseInt(input[index++]);
    
    // 初始化数组并计算对数值
    const logValues = [];
    for(let i = 0; i < N; i++){
        const num = parseFloat(input[index++]);
        logValues.push(Math.log(num)); // 计算每个数的自然对数
    }
    
    // 计算前缀和数组
    const prefixSums = new Array(N + 1).fill(0);
    for(let i = 1; i <= N; i++){
        prefixSums[i] = prefixSums[i - 1] + logValues[i - 1];
    }
    
    // 初始化最大对数平均值及对应子数组的起始位置和长度
    let maxLogAverage = -Infinity;
    let resultStartIndex = 0;
    let resultLength = L;
    
    // 初始化最小前缀和及其索引
    let minPrefix = 0.0;
    let minPrefixIndex = 0;
    
    // 遍历前缀和数组,寻找最大对数平均值的子数组
    for(let i = L; i <= N; i++){
        // 更新最小前缀和为[0, i - L]区间的最小前缀和
        if(prefixSums[i - L] < minPrefix){
            minPrefix = prefixSums[i - L];
            minPrefixIndex = i - L;
        }
        
        // 计算当前对数平均值
        const currentLogAverage = (prefixSums[i] - minPrefix) / L;
        
        // 更新最大对数平均值及对应的子数组位置和长度
        if(currentLogAverage > maxLogAverage){
            maxLogAverage = currentLogAverage;
            resultStartIndex = minPrefixIndex;
            resultLength = L;
        } else if(Math.abs(currentLogAverage - maxLogAverage) < 1e-10){
            // 如果对数平均值相同,选择长度更小的子数组
            if(L < resultLength){
                resultStartIndex = minPrefixIndex;
                resultLength = L;
            }
            // 如果长度相同,选择起始位置更靠前的子数组(无需操作,因为遍历顺序已保证)
        }
    }
    
    // 输出结果
    console.log(resultStartIndex + " " + resultLength);
});

八、C算法源码

c 复制代码
#include <stdio.h>
#include <math.h>
#include <float.h>

int main(){
    int N, L;
    // 读取数组大小N和子数组最小长度L
    scanf("%d %d", &N, &L);
    
    double log_values[N];
    // 读取数组元素并计算对数值
    for(int i = 0; i < N; i++){
        double num;
        scanf("%lf", &num);
        log_values[i] = log(num); // 计算每个数的自然对数
    }
    
    // 计算前缀和数组
    double prefix_sums[N + 1];
    prefix_sums[0] = 0.0;
    for(int i = 1; i <= N; i++){
        prefix_sums[i] = prefix_sums[i - 1] + log_values[i - 1];
    }
    
    // 初始化最大对数平均值及对应子数组的起始位置和长度
    double max_log_average = -DBL_MAX;
    int result_start_index = 0;
    int result_length = L;
    
    // 初始化最小前缀和及其索引
    double min_prefix = 0.0;
    int min_prefix_index = 0;
    
    // 遍历前缀和数组,寻找最大对数平均值的子数组
    for(int i = L; i <= N; i++){
        // 更新最小前缀和为[0, i - L]区间的最小前缀和
        if(prefix_sums[i - L] < min_prefix){
            min_prefix = prefix_sums[i - L];
            min_prefix_index = i - L;
        }
        
        // 计算当前对数平均值
        double current_log_average = (prefix_sums[i] - min_prefix) / L;
        
        // 更新最大对数平均值及对应的子数组位置和长度
        if(current_log_average > max_log_average){
            max_log_average = current_log_average;
            result_start_index = min_prefix_index;
            result_length = L;
        }
        else if(fabs(current_log_average - max_log_average) < 1e-10){
            // 如果对数平均值相同,选择长度更小的子数组
            if(L < result_length){
                result_start_index = min_prefix_index;
                result_length = L;
            }
            // 如果长度相同,选择起始位置更靠前的子数组(无需操作,因为遍历顺序已保证)
        }
    }
    
    // 输出结果
    printf("%d %d\n", result_start_index, result_length);
    
    return 0;
}

九、C++算法源码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int N, L;
    // 读取数组大小N和子数组最小长度L
    cin >> N >> L;
    
    vector<double> log_values(N);
    // 读取数组元素并计算对数值
    for(int i = 0; i < N; i++){
        double num;
        cin >> num;
        log_values[i] = log(num); // 计算每个数的自然对数
    }
    
    // 计算前缀和数组
    vector<double> prefix_sums(N + 1, 0.0);
    for(int i = 1; i <= N; i++){
        prefix_sums[i] = prefix_sums[i - 1] + log_values[i - 1];
    }
    
    // 初始化最大对数平均值及对应子数组的起始位置和长度
    double max_log_average = -1e308; // 接近负无穷
    int result_start_index = 0;
    int result_length = L;
    
    // 初始化最小前缀和及其索引
    double min_prefix = 0.0;
    int min_prefix_index = 0;
    
    // 遍历前缀和数组,寻找最大对数平均值的子数组
    for(int i = L; i <= N; i++){
        // 更新最小前缀和为[0, i - L]区间的最小前缀和
        if(prefix_sums[i - L] < min_prefix){
            min_prefix = prefix_sums[i - L];
            min_prefix_index = i - L;
        }
        
        // 计算当前对数平均值
        double current_log_average = (prefix_sums[i] - min_prefix) / L;
        
        // 更新最大对数平均值及对应子数组的位置和长度
        if(current_log_average > max_log_average){
            max_log_average = current_log_average;
            result_start_index = min_prefix_index;
            result_length = L;
        }
        else if(abs(current_log_average - max_log_average) < 1e-10){
            // 如果对数平均值相同,选择长度更小的子数组
            if(L < result_length){
                result_start_index = min_prefix_index;
                result_length = L;
            }
            // 如果长度相同,选择起始位置更靠前的子数组(无需操作,因为遍历顺序已保证)
        }
    }
    
    // 输出结果
    cout << result_start_index << " " << result_length << "\n";
    
    return 0;
}

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

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

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

相关推荐
I_Am_Me_4 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
℘团子এ14 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z19 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁43 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜43 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple1 小时前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five1 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript
临枫5411 小时前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
酷酷的威朗普1 小时前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5