华为OD机试真题中的“最大花费金额”问题是一道经典的算法题,以下是该题目的详细描述及解析:
一、题目描述
双十一众多商品进行打折销售,小明想购买自己心仪的一些物品,但由于受购买资金限制,所以他决定从众多心仪商品中购买三件,而且想尽可能的花完资金。现在请你设计一个程序帮助小明计算尽可能花费的最大资金数额。
输入:
- 第一行为一维整型数组m,数组长度小于100,数组元素记录单个商品的价格,单个商品价格小于1000。
- 第二行为购买资金的额度r,r小于100000。
输出:输出为满足上述条件的最大花费额度。
二、解题思路
这道题目主要考察组合算法以及算法优化能力。由于需要找出所有可能的商品组合(三件),然后计算每种组合的总价,再找出不超过预算的最大总价,因此时间复杂度较高。为了优化算法,可以考虑以下思路:
- 排序:首先将商品按价格从高到低排序,这样从高到低选择商品时,可以更容易地找到接近预算的组合。
- 双指针:在排序后的数组中,使用双指针法来遍历所有可能的组合。具体地,可以先固定一个商品,然后用双指针来遍历剩下的两个商品,从而避免三重循环,降低时间复杂度。
- 剪枝:在遍历过程中,如果当前组合的总价已经超过预算,则可以直接跳过该组合,继续寻找下一个可能的组合。
三、输入输出示例
输入
4
23 26 36 27
78
输出
76
四、代码实现
import java.util.Arrays;
import java.util.Scanner;
public class MaxSpending {
/**
* 计算在不超过预算的情况下,可以花费的最大金额
* 该方法通过找出数组中三个元素之和的最大值,且该值不超过预算,来解决问题
*
* @param prices 一个整数数组,包含每个商品的价格
* @param budget 整数,代表预算金额
* @return 在不超过预算的情况下,可以花费的最大金额如果无法找到满足条件的组合,则返回-1
*/
public static int maxSpending(int[] prices, int budget) {
// 检查输入数组是否为空或元素不足,如果是,返回-1
if (prices == null || prices.length < 3) {
return -1; // 处理空数组或不足三个元素的情况
}
// 对价格数组进行排序,以便后续使用双指针技术
Arrays.sort(prices); // 按价格从低到高排序
int n = prices.length;
int maxAmount = -1; // 初始化最大花费金额为-1
// 遍历所有可能的第一个商品的索引
for (int i = n - 2; i >= 0; i--) {
int left = 0; // 第二个商品的起始索引(左指针)
int right = i - 1; // 第三个商品的起始索引(右指针)
// 使用双指针遍历所有可能的第二个和第三个商品的组合
while (left < right) {
int total = prices[i] + prices[left] + prices[right];
// 如果当前组合的总价格不超过预算,则更新最大花费金额,并尝试增加总价
if (total <= budget) {
maxAmount = Math.max(maxAmount, total);
left++; // 增加总价
} else {
right--; // 减小总价
}
}
}
// 返回可以花费的最大金额
return maxAmount;
}
public static void main(String[] args) {
// 创建Scanner对象以读取输入
Scanner scanner = new Scanner(System.in);
// 直接输入一个数组
//String input = scanner.nextLine(); // 读取一行输入
// String[] inputArray = input.split(" "); // 将输入按空格分割成字符串数组
//int[] prices = new int[inputArray.length]; // 创建一个整数数组
// 将字符串数组转换为整数数组
// for (int i = 0; i < inputArray.length; i++) {
// prices[i] = Integer.parseInt(inputArray[i]);
//}
// 读取价格数组的长度
int m = scanner.nextInt();
// 初始化价格数组,这里直接使用题目给定的示例初始化,简化输入过程
int[] prices = new int[m];
// 读取价格数组元素
for (int i = 0; i < m; i++) {
prices[i] = scanner.nextInt();
}
// 读取预算
int budget = scanner.nextInt();
// 调用maxSpending函数计算在给定预算下最大花费金额,并输出结果
int result = maxSpending(prices, budget);
System.out.println("最大花费金额:" + result);
// 关闭Scanner对象
scanner.close();
}
}
五、运行示例解析
输入部分
-
输入价格数组的长度:
- 用户首先输入一个整数
4,表示价格数组中有4个元素。
- 用户首先输入一个整数
-
输入价格数组:
- 接着,用户输入四个整数
23 26 36 27,这些整数表示各个商品的价格。 - 这些价格被存储在
prices数组中,即prices = [23, 26, 36, 27]。
- 接着,用户输入四个整数
-
输入预算:
- 最后,用户输入一个整数
78,表示小明的购买预算。
- 最后,用户输入一个整数
代码执行部分
-
检查输入数组是否为空或元素不足:
- 代码首先检查
prices数组是否为空或长度小于3。在这个例子中,prices数组有4个元素,因此这一检查通过。
- 代码首先检查
-
排序价格数组:
prices数组被排序,以便后续使用双指针技术。排序后的数组为[23, 26, 27, 36]。
-
遍历所有可能的第一个商品的索引:
- 代码使用一个
for循环遍历所有可能的第一个商品的索引。由于数组已经排序,我们从价格最高的商品开始考虑(但在这个例子中,由于我们是从后往前遍历索引,所以实际上是从高到低看价格,但因为数组是升序的,所以索引越大,价格越高)。 - 循环变量
i从n - 2(即2)开始,到0结束。这意味着我们将考虑以下三个商品的组合:(36, 27, 26),(36, 27, 23),(27, 26, 23)。
- 代码使用一个
-
使用双指针遍历第二个和第三个商品的组合:
- 对于每个固定的
i值,代码使用两个指针left和right来遍历所有可能的第二个和第三个商品的组合。 - 初始时,
left指向0,right指向i - 1。 - 在循环中,计算三个商品的总价
total,并根据total与budget的比较结果来移动指针。
- 对于每个固定的
-
更新最大花费金额:
- 如果
total小于等于budget,则更新maxAmount为total和maxAmount中的较大值。 - 根据
total与budget的比较结果,移动left或right指针。
- 如果
详细的遍历过程
-
当
i = 2时(考虑组合(36, 27, 26)):left = 0,right = 1:total = 36 + 23 + 26 = 85(超过预算,right--)left = 0,right = 0:此时left不再小于right,循环结束。
-
当
i = 1时(考虑组合(36, 27, 23)):left = 0,right = 0:total = 36 + 23 + 27 = 86(超过预算,但由于left和right相遇,循环结束)
-
当
i = 0时(考虑组合(27, 26, 23)):left = 0,right = 1:total = 27 + 23 + 26 = 76(小于等于预算,更新maxAmount = 76,left++)left = 1,right = 1:此时left不再小于right,循环结束。
输出部分
- 最终,
maxAmount的值为76,这是在不超过预算78的情况下,可以花费的最大金额。 - 程序输出
"最大花费金额:76"。
总结
- 程序通过排序和双指针技术有效地找到了满足条件的最大花费金额。
- 输入的数组
[23, 26, 36, 27]和预算78导致了输出76,这是在不超过预算的情况下可以花费的最大金额。
六、注意事项
- 输入验证:虽然题目已经说明输入格式是正确的,但在实际编程中,仍然需要添加适当的输入验证来确保程序的健壮性。
- 边界条件:需要注意处理边界条件,例如当数组长度为0或预算为0时的情况。
- 性能优化:由于题目中给定的数组长度和预算范围都有限制,因此可以通过排序和双指针等方法来优化算法性能。如果数组长度或预算范围更大,可能需要考虑更高效的算法或数据结构来解决该问题。
综上所述,“最大花费金额”问题是一道考察组合算法和算法优化能力的经典题目。通过排序、双指针和剪枝等方法,可以有效地降低算法的时间复杂度并找到满足条件的最大花费金额。

1861

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



