华为OD机试真题中的“云短信平台优惠活动”题目描述如下:
一、题目描述
某云短信厂商为庆祝国庆,推出充值优惠活动。现在给出客户预算和优惠售价序列,要求计算最多可获得的短信总条数。
二、输入描述
- 第一行客户预算M,其中0 ≤ M ≤ 10^6。
- 第二行给出售价表,P1, P2,…, Pn,其中1 ≤ n ≤ 100。Pi表示充值i元获得的短信条数,1 ≤ Pi ≤ 1000。
三、输出描述
- 输出最多获得的短信总条数。
四、示例
示例1
- 输入:
6
10 20 30 40 60
- 输出:
70 - 说明:分两次充值最优,1元和5元各充一次,总条数为10 + 60 = 70。
示例2
- 输入:
18
1 2 30 40 60 84 70 80 90 150
- 输出:
252
五、解题思路
这个问题是一个经典的完全背包问题,其中每个充值方案(即物品)可以多次使用。我们需要在给定预算下,最大化可获得的短信条数。可以通过动态规划算法来解决这个问题。
- 定义一个数组dp,其中dp[j]表示在预算为j时最多能获得的短信条数。
- 初始化dp数组,dp[0] = 0,表示在预算为0时无法获得任何短信。
- 遍历每个充值方案,对于每个充值方案pi(表示充值i元获得的短信条数),更新dp数组。对于每个可能的预算j(j从i到M),如果j >= i,则尝试使用当前充值方案pi来更新dp[j],即dp[j] = max(dp[j], dp[j - i] + pi)。
- 遍历完成后,dp[M]即为最多能获得的短信条数。
六、代码实现
import java.util.Scanner;
public class CloudSMSDiscount {
/*
* 主函数
* 读取用户输入的预算和手机售价表,计算并输出在给定预算下最多能获得的短信总条数
* @param args 命令行参数
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取客户预算
int M = scanner.nextInt();
scanner.nextLine(); // 消耗掉换行符
// 读取售价表
String[] n = scanner.nextLine().split(" ");
// 将售价表字符串转换为整数数组
int[] prices = new int[n.length];
for (int i = 0; i < n.length; i++) {
prices[i] = Integer.parseInt(n[i]);
}
// 动态规划数组,dp[j]表示在预算为j时最多能获得的短信条数
int[] dp = new int[M + 1];
// 遍历每个充值方案
for (int i = 0; i < n.length; i++) {
int cost = i + 1; // 售价表中的索引从0开始,但充值金额从1开始
int smsCount = prices[i];
// 更新dp数组
for (int j = cost; j <= M; j++) {
dp[j] = Math.max(dp[j], dp[j - cost] + smsCount);
}
}
// 输出最多获得的短信总条数
System.out.println(dp[M]);
scanner.close();
}
}
七、注意事项
-
输入处理:
- 读取客户预算
M。 - 读取售价表长度
n,然后读取n个售价值存入数组prices。
- 读取客户预算
-
动态规划数组:
dp数组的长度为M + 1,因为预算M是一个上限值,数组索引从0到M。- 初始化时,
dp数组的所有元素默认为0(Java中int数组默认初始化为0)。
-
状态转移:
- 遍历每个充值方案,用
cost表示充值金额(从1开始,因为售价表中的索引从0开始)。 - 用
smsCount表示充值后获得的短信条数。 - 更新
dp数组时,从cost开始遍历到M,对于每个j,尝试使用当前充值方案来更新dp[j]。
- 遍历每个充值方案,用
-
输出结果:
- 最后,
dp[M]存储的是在预算为M时最多能获得的短信条数。
- 最后,
八、测试用例解析
代码解析
-
主函数(
main方法):- 初始化
Scanner对象以读取用户输入。 - 读取客户预算
M。 - 消耗掉输入缓冲区中的换行符,避免影响后续
nextLine()的读取。 - 读取售价表,使用
nextLine()获取一行输入,然后使用split(" ")将其拆分为字符串数组。 - 将字符串数组转换为整数数组
prices。 - 初始化动态规划数组
dp,长度为M + 1,所有元素默认初始化为0。 - 遍历每个充值方案,更新
dp数组以记录在给定预算下最多能获得的短信条数。 - 输出
dp[M],即在预算为M时最多能获得的短信条数。 - 关闭
Scanner对象。
- 初始化
-
动态规划部分:
- 对于每个充值方案(由
prices数组中的值表示),将其索引i加1作为充值金额cost(因为售价表中的索引从0开始,但充值金额从1开始)。 smsCount表示充值cost元后获得的短信条数。- 对于每个可能的预算
j(从cost到M),尝试使用当前充值方案来更新dp[j]。如果充值cost元后获得的短信条数加上剩余预算j - cost时能获得的最多短信条数大于当前dp[j]的值,则更新dp[j]。
- 对于每个充值方案(由
运行示例解析
输入:
6
10 20 30 40 60
执行流程:
- 读取预算
M = 6。 - 读取售价表,转换为整数数组
prices = [10, 20, 30, 40, 60]。 - 初始化动态规划数组
dp,长度为7(M + 1),初始值为[0, 0, 0, 0, 0, 0, 0]。 - 遍历每个充值方案:
- 当
i = 0时,cost = 1,smsCount = 10。由于cost = 1小于M,更新dp数组:dp[1] = Math.max(dp[1], dp[0] + 10) = 10dp[2]至dp[6]保持不变,因为cost = 1不足以覆盖这些预算。
- 当
i = 1时,cost = 2,smsCount = 20。更新dp数组:dp[2] = Math.max(dp[2], dp[0] + 20) = 20dp[3] = Math.max(dp[3], dp[1] + 20) = 30(因为dp[1]已被更新为10)dp[4]至dp[6]保持不变。
- 类似地,继续遍历
i = 2, 3, 4,更新dp数组:- 当
i = 2时,cost = 3,smsCount = 30,更新dp[3]至dp[6]。 - 当
i = 3时,cost = 4,smsCount = 40,更新dp[4]至dp[6]。 - 当
i = 4时,cost = 5,smsCount = 60,仅更新dp[6],因为dp[5]在之前的迭代中已被更新。
- 当
- 当
- 最终,
dp数组变为[0, 10, 20, 30, 40, 60, 70]。 - 输出
dp[6] = 70,即在预算为6时最多能获得的短信条数。
因此,对于输入6 10 20 30 40 60,程序输出70,这是正确的结果。

5755

被折叠的 条评论
为什么被折叠?



