华为OD机试真题 - 取零食 - 2024 E卷(Java、Python、JavaScript、C、C++合集)

一、题目描述

小朋友考试得第一名就可以得到零食奖励。

现在价格A、B、C、D、E、…,元商品名A1、B1、C1、D1、E1、…,小朋友的喜爱度依次为A2、B2、C2、D2、E2、…。

请返回选取x元零食可以达到的最大喜爱度。

二、输入描述

第一行输入为x和N,x为可使用的钱的总额,N为零食种类数。

0 ≤ x ≤ 1000
0 ≤ N ≤ 100

第二行开始为零食属性,每行有三个整型数值,分别代表零食的价格、数量和喜爱度。

零食价格(0,100)
零食个数[0,10000]
零食喜爱度[0,10000]

三、输出描述

最大喜爱度

四、测试用例

测试用例1:

1、输入

10 2
2 4 3
3 3 4

2、输出

14

3、说明

可选第1种零食最多 4 个,第2种最多 3 个。最佳选择为:选 2 个第2种(花费 6 元,喜爱度 8)和 2 个第1种(花费 4 元,喜爱度 6),总计花费 10 元,总喜爱度 14。输出 14。

测试用例2:

1、输入

6 7
3 1 8
4 1 2
3 1 1
9 1 7
4 1 1
5 1 8
4 1 4

2、输出

9

3、说明

6元可以购买两个3元的零食,喜爱度综合为8+1=9

五、解题思路

本题本质上是一个有限背包问题。题目给定零食的价格、可购买数量及喜爱度,要求在不超过总金额 x 的条件下,使所选零食的总喜爱度最大。由于每种零食的数量有限,直接使用 0/1 背包方法会遇到数量限制问题。

对于每种零食,如果数量较大,可以利用二进制拆分的技巧,将数量拆分为若干个“份”,每份视为一个 0/1 物品。例如,若数量为 13,则可拆分为 1, 2, 4, 6(6 = 13-1-2-4)。这样每个子问题就转换为 0/1 背包问题,从而降低时间复杂度。

使用 dp 数组,其中 dp[j] 表示在花费 j 元时能够获得的最大喜爱度。由于总金额 x 最大为 1000,使用一维数组即可。

当每种零食数量较大时,如果直接循环每个单位可能超时。采用二进制拆分,将数量拆分为若干个物品,转化为 0/1 背包问题,可有效减少状态转移次数。

六、Java算法源码

public class OdTest {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt(); // 可用的总金额
        int N = sc.nextInt(); // 零食种类数
        int[] dp = new int[x + 1]; // dp[j]表示花费j元时能获得的最大喜爱度
        for (int i = 0; i < N; i++) {
            int price = sc.nextInt(); // 零食价格
            int count = sc.nextInt(); // 零食数量
            int value = sc.nextInt(); // 零食喜爱度
            int k = 1;
            // 利用二进制拆分,将count分解为多个01背包问题的物品
            while (count > 0) {
                int num = Math.min(k, count); // 当前拆分的数量
                int cost = price * num;       // 当前拆分的总价格
                int val = value * num;        // 当前拆分的总喜爱度
                // 从后向前遍历更新dp数组(防止重复使用)
                for (int j = x; j >= cost; j--) {
                    dp[j] = Math.max(dp[j], dp[j - cost] + val);
                }
                count -= num;
                k *= 2;
            }
        }
        System.out.println(dp[x]); // 输出最大喜爱度
        sc.close();
    }
}

七、Python算法源码

# Python代码实现有限背包问题,采用二进制拆分方法
import sys  # 导入sys模块用于读取标准输入

def main():
    # 读取所有输入数据,并将其转换为整数列表
    data = list(map(int, sys.stdin.read().split()))
    if not data:
        return
    x = data[0]   # 总金额
    N = data[1]   # 零食种类数
    dp = [0] * (x + 1)  # 初始化dp数组,dp[j]表示花费j元时获得的最大喜爱度
    index = 2  # 从数据列表的第3个数字开始为各零食属性
    # 遍历每种零食
    for _ in range(N):
        price = data[index]     # 零食价格
        count = data[index+1]   # 零食数量
        value = data[index+2]   # 零食喜爱度
        index += 3  # 更新索引,指向下一组零食数据
        k = 1
        # 利用二进制拆分,将count拆分为多个0/1背包问题的物品
        while count > 0:
            num = min(k, count)  # 当前拆分的数量
            cost = price * num   # 当前拆分的总价格
            val = value * num    # 当前拆分的总喜爱度
            # 从后向前遍历更新dp数组,防止重复使用同一物品
            for j in range(x, cost - 1, -1):
                dp[j] = max(dp[j], dp[j - cost] + val)
            count -= num  # 更新剩余数量
            k *= 2        # 递增倍数
    # 输出最终结果
    print(dp[x])

if __name__ == "__main__":
    main()

八、JavaScript算法源码

'use strict';
 
// Node.js实现有限背包问题,采用二进制拆分方法
process.stdin.resume();            // 启动标准输入
process.stdin.setEncoding('utf8'); // 设置输入编码为utf8
let inputData = '';              // 用于存储所有输入数据

// 监听数据输入事件
process.stdin.on('data', function(chunk) {
    inputData += chunk;
});
 
// 输入结束后处理数据
process.stdin.on('end', function() {
    // 按空白字符拆分输入数据,并转换为数字数组
    let data = inputData.trim().split(/\s+/).map(Number);
    if(data.length === 0) return;
    let x = data[0];   // 可用的总金额
    let N = data[1];   // 零食种类数
    let dp = new Array(x + 1).fill(0);  // 初始化dp数组,dp[j]表示花费j元时获得的最大喜爱度
    let index = 2;  // 数据起始位置(零食属性从此处开始)
    // 遍历每种零食
    for(let i = 0; i < N; i++){
        let price = data[index];    // 零食价格
        let count = data[index+1];  // 零食数量
        let value = data[index+2];  // 零食喜爱度
        index += 3;  // 移动到下一组零食数据
        let k = 1;
        // 二进制拆分,将count拆分为多个0/1背包问题的物品
        while(count > 0){
            let num = Math.min(k, count);  // 当前拆分的数量
            let cost = price * num;         // 当前拆分的总价格
            let val = value * num;          // 当前拆分的总喜爱度
            // 从后向前遍历,更新dp数组(防止重复使用)
            for(let j = x; j >= cost; j--){
                dp[j] = Math.max(dp[j], dp[j - cost] + val);
            }
            count -= num;  // 更新剩余数量
            k *= 2;        // 更新倍数
        }
    }
    // 输出最终结果
    console.log(dp[x]);
});

九、C算法源码

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

// 定义一个函数用于返回两个数中的较大值
int max(int a, int b) {
    return a > b ? a : b;
}

int main(){
    int x, N;
    // 读取总金额x和零食种类数N
    if(scanf("%d %d", &x, &N) != 2) return 1;
    
    // 动态分配dp数组,dp[j]表示花费j元时能获得的最大喜爱度,初始值为0
    int *dp = (int*)calloc(x + 1, sizeof(int));
    
    // 遍历每种零食
    for(int i = 0; i < N; i++){
        int price, count, value;
        // 读取当前零食的价格、数量和喜爱度
        scanf("%d %d %d", &price, &count, &value);
        int k = 1;
        // 二进制拆分,将count拆分为多个0/1背包问题的物品
        while(count > 0){
            int num = k < count ? k : count; // 当前拆分的数量(取k和剩余数量中的较小值)
            int cost = price * num;          // 当前拆分的总价格
            int val = value * num;           // 当前拆分的总喜爱度
            // 从后向前遍历更新dp数组,保证每个子物品只使用一次
            for(int j = x; j >= cost; j--){
                dp[j] = max(dp[j], dp[j - cost] + val);
            }
            count -= num; // 更新剩余数量
            k *= 2;       // 更新倍数
        }
    }
    
    // 输出最大喜爱度结果
    printf("%d\n", dp[x]);
    free(dp); // 释放动态分配的内存
    return 0;
}

十、C++算法源码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    int x, N;
    // 读取总金额x和零食种类数N
    cin >> x >> N;
    vector<int> dp(x + 1, 0); // 初始化dp数组,dp[j]表示花费j元时获得的最大喜爱度
    
    // 遍历每种零食
    for (int i = 0; i < N; i++) {
        int price, count, value;
        // 读取当前零食的价格、数量和喜爱度
        cin >> price >> count >> value;
        int k = 1;
        // 利用二进制拆分,将count拆分为多个0/1背包问题的物品
        while (count > 0) {
            int num = min(k, count); // 当前拆分的数量
            int cost = price * num;  // 当前拆分的总价格
            int val = value * num;   // 当前拆分的总喜爱度
            // 从后向前遍历,更新dp数组(防止重复使用同一物品)
            for (int j = x; j >= cost; j--) {
                dp[j] = max(dp[j], dp[j - cost] + val);
            }
            count -= num; // 更新剩余数量
            k *= 2;       // 更新倍数
        }
    }
    
    // 输出最终的最大喜爱度
    cout << dp[x] << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值