题目描述
小R计划进行一次从地点A到地点B的徒步旅行,这次旅行总共需要 天。为了保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R每天都会经过一个补给站,可以在此购买食物进行补充。然而,每个补给站的食物价格各不相同,每份食物的价格由数组 给出,其中 表示第 天补给站的食物单价。此外,小R最多只能同时携带 份食物。
小R希望在保证每天都有食物的前提下,以最小的花费完成这次徒步旅行。你的任务是帮助小R计算出最低的总花费。
解题思路
解题思路
这是一道典型的动态规划问题,需要考虑在每一天购买多少食物才能以最小的成本完成整个旅程。关键在于决策小R在每个补给站购买的食物数量,使得在满足每天消耗1份食物和背包容量不超过 的限制下,总花费最小。
我们可以使用动态规划来解决这个问题,定义状态和状态转移方程如下:
-
状态定义:设 表示在第 i 天购物前,背包中有 j 份食物,完成剩余旅程所需的最小总花费。
-
初始状态:
-
对于最后一天(第 天),我们需要确保小R有足够的食物完成当天的消耗。
- 如果此时背包中有0份食物( ),则必须购买1份食物,花费为 。
- 如果背包中有食物( ),则不需要购买,花费为0。
-
-
状态转移方程:
- 对于第 天,考虑当前背包中有 份食物,可能的决策是购买 份食物( ),使得背包中的食物数量不超过 。
- 每次购买后,背包中的食物数量变为 (消耗1份食物),下一状态为 。
- 我们需要在所有可能的 中选择使得总花费 最小的方案。
-
目标:计算 ,即从第0天开始,背包中没有食物的情况下完成旅程的最小总花费。
代码实现
public class Main {
public static int solution(int n, int k, int[] data) {
// dp[i][j]表示:在第i天结束时,背包中有j份食物,完成剩余旅程的最小花费
int[][] dp = new int[n][k + 1];
// 初始化最后一天的dp数组
for (int foodCnt = 0; foodCnt <= k; foodCnt++) {
if (foodCnt == 0) {
// 背包为空,需要购买1份食物
dp[n - 1][foodCnt] = data[n - 1];
} else {
// 背包有食物,不需要购买
dp[n - 1][foodCnt] = 0;
}
}
// 从倒数第二天开始递推
for (int nthDay = n - 2; nthDay >= 0; nthDay--) {
for (int curFoodCnt = 0; curFoodCnt <= k; curFoodCnt++) {
int minCost = Integer.MAX_VALUE;
// 枚举在当前补给站购买的食物数量
for (int addFoodCnt = 0; addFoodCnt + curFoodCnt <= k; addFoodCnt++) {
// 购买的食物花费
int needMoney = addFoodCnt * data[nthDay];
// 计算下一状态的食物数量
int nextFoodCnt = addFoodCnt + curFoodCnt - 1;
if (nextFoodCnt >= 0) {
// 更新最小花费
minCost = Math.min(minCost, needMoney + dp[nthDay + 1][nextFoodCnt]);
}
}
dp[nthDay][curFoodCnt] = minCost;
}
}
// 返回从第0天开始,背包为空的最小总花费
return dp[0][0];
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(5, 2, new int[]{1, 2, 3, 3, 2}) == 9);
System.out.println(solution(6, 3, new int[]{4, 1, 5, 2, 1, 3}) == 9);
System.out.println(solution(4, 1, new int[]{3, 2, 4, 1}) == 10);
}
}
代码详解
-
dp数组的定义:使用一个二维数组 来存储状态,其中 是天数,是可能的食物数量(从0到)。
-
初始化:
- 对于最后一天,如果背包中没有食物( ),必须购买1份食物,花费为 。
- 如果背包中有食物( ),则不需要购买,花费为0。
-
动态规划递推:
- 外层循环从第 天开始递推到第0天。
- 内层循环遍历当前可能的食物数量 。
- 对于每个可能的购买数量 ,计算购买花费 和下一状态的食物数量 。
- 更新 为最小的总花费。
-
返回结果:最后返回 ,即从第0天开始,背包为空的情况下的最小总花费。
时间复杂度和空间复杂度分析
-
时间复杂度:
- 外层循环遍历 天,时间复杂度为 。
- 内层循环遍历背包可能的食物数量,复杂度为 。
- 最内层循环遍历可能的购买数量,复杂度为 。
- 总的时间复杂度为
-
空间复杂度:
- 使用了一个二维数组 ,空间复杂度为
总结
这道题目考察了动态规划的应用,特别是多维状态下的最优化问题。通过精确地定义状态和状态转移方程,我们能够有效地解决问题。关键在于理解在每一天可能的决策,以及如何在满足约束条件下找到最小的总花费。时间和空间复杂度虽然较高,但在 和 不大的情况下,仍然是可行的。