目录
题目
某大型樱桃加工厂使用自动化机械扫描了一批樱桃的尺寸大小。现在获得了直径范围 [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 核心思路
- 枚举所有可能的分组方案
- 计算每种方案的标准差
- 选择标准差最小的方案
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】