2025.05.07华为暑期实习(留学生)机试真题【樱桃等级筛选】Java/Python/C++/JS/C 实现

目录

题目

思路

Code


题目

某大型樱桃加工厂使用自动化机械扫描了一批樱桃的尺寸大小。现在获得了直径范围 [L,H]各个区间所有的樱桃个数统计。现在需要通过m个等级(m < H-L)来筛选不同尺寸大小的樱桃,筛选后需使得各等级内的樱桃数目的 标准差最小。
输入描述
第一行输入两个数字,第一个数字表示樱桃的总组数 n(2 <n <= 20),第二个数字 m 表示需要获取的等级数目 a(2<a<n)
第二行输入一个长度为 H-L+1的数列 A,a<H-L+1,其中的第i个元素 ai 表示直径为L+i樱桃个数
(0<=i<=H-L),(0 < ai < 100)
输出描述
输出长度为 m 的数列 B ,其中的第1 个元素 b0,表示顺序从 A 中取 b0 个元素,将该尺寸范围内的樱桃作为一个分类等级;第2个元素b1表示顺序从 A 中起始点b0开始取b1个元素,将该尺寸范围内的樱桃作为一个分类等级,依次类推。


示例1:
输入:
9 3
1 2 3 4 5 6 7 8 9

输出:
5 2 2 
说明:
要把9组樱桃分为三组,使得三组樱桃数量和的标准差最小顺序取数列5个元素和为1+2+3+4+5 =15
再顺序取数列2个元素和 6+7= 13
再顺序取数列2个元素和 8+9=17
[15, 13, 17]的平均值为 15,标准差为所有筛选方案中的最小值。


示例2:
输入:
10 4
16 40 37 20 18 30 18 60 50 37

输出:
3 3 2 2
说明:
顺序取数列 3 个元素和为 16 + 40 + 37 = 93
再顺序取数列3个元素和 20+18+30 = 68
再顺序取数列 2个元素和 18+60= 78
再顺序取数列2个元素和 50+37=87
[93,68,78,87]的平均值为81.5,标准差为所有筛选方案中的最小值.

提示
测试样例保证输出答案唯一

思路

1. 题目理解

1.1 输入内容

  • 第一行:
  • n:樱桃总组数(2 < n <= 20)
  • m:需要的等级数目(2 < m < n)
  • 第二行:
  • 长度为n的数列A
  • 每个元素ai表示对应尺寸的樱桃数量(0 < ai < 100)

1.2 输出要求

  • 长度为m的数列B
  • bi表示第i个等级包含的连续元素个数
  • 所有bi的和等于n
  • 按bi划分的各组和的标准差最小

1.3 约束条件

  • 必须按顺序分组
  • 每组至少包含一个元素
  • 所有组必须覆盖整个数列
  • 保证答案唯一

2. 问题建模

2.1 算法选择

  • 动态规划求解最优分组
  • 状态定义:dp[i][j]表示前i个数分成j组的最优解
  • 需要记录分组方案

2.2 核心思路

  1. 枚举所有可能的分组方案
  1. 计算每种方案的标准差
  1. 选择标准差最小的方案

3. 实现要点

3.1 关键技术点

  • 使用动态规划解决最优化问题
  • 记录路径以重建最优解
  • 剪枝优化搜索空间

3.2 优化细节

  • 使用字典存储状态,避免大数组
  • 预计算区间和,减少重复计算
  • 提前剪枝无效方案

3.3 复杂度分析

  • 时间复杂度:O(n²m)
  • 空间复杂度:O(nm)

Python

import math
from typing import List

def calculate_std(groups: List[int]) -> float:
    """计算一组数的标准差"""
    n = len(groups)
    if n == 0:
        return float('inf')
    
    mean = sum(groups) / n
    variance = sum((x - mean) ** 2 for x in groups) / n
    return math.sqrt(variance)

def find_min_std_groups(nums: List[int], m: int) -> List[int]:
    n = len(nums)
    
    # dp[i][j]表示前i个数分成j组的最小标准差
    dp = {}  # 使用字典存储状态
    # path[i][j]存储分组方案
    path = {}
    
    def get_sum(start: int, count: int) -> int:
        """获取从start开始count个数的和"""
        return sum(nums[start:start+count])
    
    def dp_solve(pos: int, groups_left: int) -> tuple:
        """
        动态规划求解
        pos: 当前位置
        groups_left: 剩余需要分的组数
        返回:(最小标准差, 分组方案)
        """
        if groups_left == 1:
            # 剩余所有数必须分为一组
            if pos < n:
                return calculate_std([get_sum(pos, n-pos)]), [n-pos]
            return float('inf'), []
        
        if pos >= n or groups_left <= 0:
            return float('inf'), []
        
        key = (pos, groups_left)
        if key in dp:
            return dp[key], path[key]
        
        min_std = float('inf')
        best_path = []
        
        # 枚举当前组的长度
        for i in range(1, n-pos-(groups_left-2)):  # 确保剩余数量足够分配
            current_group_sum = get_sum(pos, i)
            next_std, next_path = dp_solve(pos+i, groups_left-1)
            
            if next_std != float('inf'):
                # 计算当前方案的标准差
                all_groups = [current_group_sum] + [get_sum(pos+i+sum(next_path[:j]), next_path[j]) 
                                                  for j in range(len(next_path))]
                current_std = calculate_std(all_groups)
                
                if current_std < min_std:
                    min_std = current_std
                    best_path = [i] + next_path
        
        dp[key] = min_std
        path[key] = best_path
        return min_std, best_path
    
    # 求解并返回分组方案
    _, result = dp_solve(0, m)
    return result

def main():
    # 读取输入
    n, m = map(int, input().split())
    nums = list(map(int, input().split()))
    
    # 计算并输出结果
    result = find_min_std_groups(nums, m)
    print(*result)

if __name__ == "__main__":
    main()

 Java

import java.util.*;

/**
 * 樱桃分组问题的动态规划解法
 */
public class Solution {
    private static Map<String, Double> dp;
    private static Map<String, Integer> path;
    private static int[] nums;
    
    /**
     * 计算数组的标准差
     * @param groups 数组
     * @return 标准差
     */
    private static double calcStd(List<Integer> groups) {
        if (groups.isEmpty()) {
            return Double.MAX_VALUE;
        }
        double mean = groups.stream().mapToDouble(Integer::doubleValue).average().getAsDouble();
        double variance = groups.stream()
            .mapToDouble(x -> Math.pow(x - mean, 2))
            .average()
            .getAsDouble();
        return Math.sqrt(variance);
    }
    
    /**
     * 获取子数组和
     * @param start 起始位置
     * @param length 长度
     * @return 子数组和
     */
    private static int getSum(int start, int length) {
        int sum = 0;
        for (int i = start; i < start + length; i++) {
            sum += nums[i];
        }
        return sum;
    }
    
    /**
     * 动态规划核心函数
     * @param pos 当前位置
     * @param k 剩余需要分的组数
     * @return 最小标准差
     */
    private static double dpSolve(int pos, int k, int n) {
        if (k == 1) {
            List<Integer> groups = new ArrayList<>();
            groups.add(getSum(pos, n - pos));
            return calcStd(groups);
        }
        
        String key = pos + "," + k;
        if (dp.containsKey(key)) {
            return dp.get(key);
        }
        
        double minStd = Double.MAX_VALUE;
        int bestLen = 0;
        
        for (int length = 1; length <= n - pos - (k - 1); length++) {
            int currSum = getSum(pos, length);
            double nextStd = dpSolve(pos + length, k - 1, n);
            List<Integer> groups = new ArrayList<>();
            groups.add(currSum);
            double currStd = calcStd(groups);
            
            if (nextStd < minStd) {
                minStd = nextStd;
                bestLen = length;
            }
        }
        
        dp.put(key, minStd);
        path.put(key, bestLen);
        return minStd;
    }
    
    /**
     * 主函数
     * @param n 樱桃总组数
     * @param m 需要的等级数目
     * @param numbers 樱桃数量数组
     * @return 最优分组方案
     */
    public static List<Integer> solve(int n, int m, int[] numbers) {
        nums = numbers;
        dp = new HashMap<>();
        path = new HashMap<>();
        
        dpSolve(0, m, n);
        
        List<Integer> result = new ArrayList<>();
        int pos = 0;
        for (int i = 0; i < m; i++) {
            String key = pos + "," + (m - i);
            int length = path.get(key);
            result.add(length);
            pos += length;
        }
        
        return result;
    }
}

 C++

#include <iostream>
#include <vector>
#include <map>
#include <cmath>
#include <string>
#include <sstream>

using namespace std;

/**
 * 樱桃分组问题的动态规划解法
 */
class Solution {
private:
    // 存储中间结果的map
    map<string, double> dp;
    map<string, int> path;
    vector<int> nums;
    
    /**
     * 计算数组的标准差
     */
    double calcStd(const vector<int>& groups) {
        if (groups.empty()) return numeric_limits<double>::max();
        
        double sum = 0;
        for (int x : groups) sum += x;
        double mean = sum / groups.size();
        
        double variance = 0;
        for (int x : groups) {
            variance += pow(x - mean, 2);
        }
        variance /= groups.size();
        
        return sqrt(variance);
    }
    
    /**
     * 获取子数组和
     */
    int getSum(int start, int length) {
        int sum = 0;
        for (int i = start; i < start + length; i++) {
            sum += nums[i];
        }
        return sum;
    }
    
    /**
     * 动态规划核心函数
     */
    double dpSolve(int pos, int k, int n) {
        if (k == 1) {
            vector<int> groups = {getSum(pos, n - pos)};
            return calcStd(groups);
        }
        
        stringstream ss;
        ss << pos << "," << k;
        string key = ss.str();
        
        if (dp.find(key) != dp.end()) {
            return dp[key];
        }
        
        double minStd = numeric_limits<double>::max();
        int bestLen = 0;
        
        for (int length = 1; length <= n - pos - (k - 1); length++) {
            int currSum = getSum(pos, length);
            double nextStd = dpSolve(pos + length, k - 1, n);
            vector<int> groups = {currSum};
            double currStd = calcStd(groups);
            
            if (nextStd < minStd) {
                minStd = nextStd;
                bestLen = length;
            }
        }
        
        dp[key] = minStd;
        path[key] = bestLen;
        return minStd;
    }
    
public:
    /**
     * 主函数
     */
    vector<int> solve(int n, int m, vector<int>& numbers) {
        nums = numbers;
        dp.clear();
        path.clear();
        
        dpSolve(0, m, n);
        
        vector<int> result;
        int pos = 0;
        for (int i = 0; i < m; i++) {
            stringstream ss;
            ss << pos << "," << (m - i);
            string key = ss.str();
            int length = path[key];
            result.push_back(length);
            pos += length;
        }
        
        return result;
    }
};

 JavaScript

/**
 * 樱桃分组问题的动态规划解法
 */
class CherryGrouping {
    /**
     * 计算数组的标准差
     * @param {number[]} groups - 需要计算标准差的数组
     * @returns {number} 标准差
     */
    static calcStd(groups) {
        if (!groups.length) return Infinity;
        
        const mean = groups.reduce((a, b) => a + b, 0) / groups.length;
        const variance = groups.reduce((sum, val) => {
            return sum + Math.pow(val - mean, 2);
        }, 0) / groups.length;
        
        return Math.sqrt(variance);
    }

    /**
     * 解决樱桃分组问题
     * @param {number} n - 樱桃总组数
     * @param {number} m - 需要的等级数目
     * @param {number[]} nums - 樱桃数量数组
     * @returns {number[]} 最优分组方案
     */
    static solve(n, m, nums) {
        // dp存储中间状态
        const dp = new Map();
        // path存储最优选择
        const path = new Map();

        /**
         * 获取子数组和
         * @param {number} start - 起始位置
         * @param {number} length - 长度
         * @returns {number} 子数组和
         */
        const getSum = (start, length) => {
            let sum = 0;
            for (let i = start; i < start + length; i++) {
                sum += nums[i];
            }
            return sum;
        };

        /**
         * 动态规划核心函数
         * @param {number} pos - 当前位置
         * @param {number} k - 剩余需要分的组数
         * @returns {number} 最小标准差
         */
        const dpSolve = (pos, k) => {
            // 基础情况:剩余所有数必须分为一组
            if (k === 1) {
                const groups = [getSum(pos, n - pos)];
                return this.calcStd(groups);
            }

            const key = `${pos},${k}`;
            if (dp.has(key)) {
                return dp.get(key);
            }

            let minStd = Infinity;
            let bestLen = 0;

            // 枚举当前组的长度
            for (let length = 1; length <= n - pos - (k - 1); length++) {
                const currSum = getSum(pos, length);
                const nextStd = dpSolve(pos + length, k - 1);
                const currStd = this.calcStd([currSum]);

                if (nextStd < minStd) {
                    minStd = nextStd;
                    bestLen = length;
                }
            }

            dp.set(key, minStd);
            path.set(key, bestLen);
            return minStd;
        };

        // 从位置0开始,分m组
        dpSolve(0, m);

        // 重建最优解路径
        const result = [];
        let pos = 0;
        for (let i = 0; i < m; i++) {
            const key = `${pos},${m - i}`;
            const length = path.get(key);
            result.push(length);
            pos += length;
        }

        return result;
    }
}

// 使用示例
function main() {
    // 示例1
    const n1 = 9, m1 = 3;
    const nums1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    console.log("示例1结果:", CherryGrouping.solve(n1, m1, nums1));  // 预期输出: [5, 2, 2]

    // 示例2
    const n2 = 10, m2 = 4;
    const nums2 = [16, 40, 37, 20, 18, 30, 18, 60, 50, 37];
    console.log("示例2结果:", CherryGrouping.solve(n2, m2, nums2));  // 预期输出: [3, 3, 2, 2]
}

// 运行测试
main();

 C语言

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

#define MAX_N 20
#define MAX_M 19
#define MAX_KEY 50

/**
 * 存储DP状态的结构体
 */
struct DPState {
    double std;
    int length;
};

/**
 * 计算数组的标准差
 */
double calc_std(int* groups, int size) {
    if (size == 0) return INFINITY;
    
    double sum = 0;
    for (int i = 0; i < size; i++) {
        sum += groups[i];
    }
    double mean = sum / size;
    
    double variance = 0;
    for (int i = 0; i < size; i++) {
        variance += pow(groups[i] - mean, 2);
    }
    variance /= size;
    
    return sqrt(variance);
}

/**
 * 获取子数组和
 */
int get_sum(int* nums, int start, int length) {
    int sum = 0;
    for (int i = start; i < start + length; i++) {
        sum += nums[i];
    }
    return sum;
}

/**
 * 动态规划核心函数
 */
struct DPState dp_solve(int* nums, int n, int pos, int k, 
                       struct DPState dp[MAX_N][MAX_M]) {
    if (k == 1) {
        struct DPState state;
        int groups[1] = {get_sum(nums, pos, n - pos)};
        state.std = calc_std(groups, 1);
        state.length = n - pos;
        return state;
    }
    
    if (dp[pos][k-1].length != -1) {
        return dp[pos][k-1];
    }
    
    struct DPState best_state;
    best_state.std = INFINITY;
    best_state.length = -1;
    
    for (int length = 1; length <= n - pos - (k - 1); length++) {
        int curr_sum = get_sum(nums, pos, length);
        struct DPState next_state = dp_solve(nums, n, pos + length, k - 1, dp);
        int groups[1] = {curr_sum};
        double curr_std = calc_std(groups, 1);
        
        if (next_state.std < best_state.std) {
            best_state.std = next_state.std;
            best_state.length = length;
        }
    }
    
    dp[pos][k-1] = best_state;
    return best_state;
}

/**
 * 主函数
 */
void solve_cherry_groups(int n, int m, int* nums, int* result) {
    struct DPState dp[MAX_N][MAX_M];
    for (int i = 0; i < MAX_N; i++) {
        for (int j = 0; j < MAX_M; j++) {
            dp[i][j].length = -1;
        }
    }
    
    dp_solve(nums, n, 0, m, dp);
    
    int pos = 0;
    for (int i = 0; i < m; i++) {
        result[i] = dp[pos][m-i-1].length;
        pos += result[i];
    }
}

【华为od机试真题Python+JS+Java合集】【超值优惠】:Py/JS/Java合集

【华为od机试真题Python】:Python真题题库

【华为od机试真题JavaScript】:JavaScript真题题库

【华为od机试真题Java】:Java真题题库

【华为od机试真题C++】:C++真题题库

【华为od机试真题C语言】:C语言真题题库

【华为od面试手撕代码题库】:面试手撕代码题库

【华为校招&实习机试面试交流群:1048120678】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MISAYAONE

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

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

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

打赏作者

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

抵扣说明:

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

余额充值