AI 刷题2 徒步旅行中的补给问题 题解 | 豆包MarsCode AI刷题

42 阅读3分钟

问题背景

小R正在进行一次从地点A到地点B的徒步旅行,总共需要 N 天。为了在旅途中保持充足的能量,小R每天必须消耗 1 份食物。途中每天都会经过一个补给站,每个补给站的食物价格不同。此外,小R最多只能携带 K 份食物。因此,为了完成旅行,小R需要在保证每天都有食物的情况下,以最小的花费完成这次旅程。

核心问题

  1. 约束条件

    • 每天消耗 1 份食物。
    • 背包最多容纳 K 份食物。
  2. 目标

    • 通过合理的购买策略,最小化购买食物的花费。

输入输出描述

  • 输入:

    • n: 总旅行天数。
    • k: 食物背包最大容量。
    • data: 长度为 n 的数组,每个元素代表某天补给站的单价。
  • 输出:

    • 完成整个旅程的最小花费。

示例

样例 1:

  • 输入:n = 5, k = 2, data = [1, 2, 3, 3, 2]
  • 输出:9

样例 2:

  • 输入:n = 6, k = 3, data = [4, 1, 5, 2, 1, 3]
  • 输出:9

样例 3:

  • 输入:n = 4, k = 1, data = [3, 2, 4, 1]
  • 输出:10

解题思路

1. 动态规划建模

这个问题可以用动态规划来解决。我们用一个二维数组 dp[i][j] 表示以下含义:

  • i: 第 i 天。
  • j: 到第 i 天结束时背包里剩余的食物数量。
  • dp[i][j]: 表示从第 1 天到第 i 天,且在第 i 天背包剩余 j 份食物时的最小花费。

状态转移方程

假设在第 i-1 天背包中剩余了 l 份食物,现在要转移到第 i 天背包剩余 j 份食物的状态:

  • 需要购买的食物量为 j - l + 1(因为每天消耗 1 份食物)。
  • i 天的总花费为: dp[i][j]=min⁡(dp[i−1][l]+(j−l+1)×data[i−1])dp[i][j] = \min(dp[i-1][l] + (j - l + 1) \times data[i-1])dp[i][j]=min(dp[i−1][l]+(j−l+1)×data[i−1]) 其中,j - l + 1 需要满足: 0≤j−l+1≤k0 \leq j - l + 1 \leq k0≤j−l+1≤k

边界条件

  • 第 0 天:dp[0][0] = 0(出发时没有花费)。
  • dp[0][j](其他初始值):设为无穷大,因为不可能在出发时剩余食物。

最终答案

最终在第 n 天,dp[n][0] 即为小R完成旅行的最小花费。


2. 算法实现

以下是完整代码的实现:

python
复制代码
def solution(n, k, data):
    # 初始化动态规划数组
    dp = [[float('inf')] * (k + 1) for _ in range(n + 1)]
    dp[0][0] = 0  # 初始条件:第 0 天无花费
    
    # 状态转移
    for i in range(1, n + 1):  # 遍历每一天
        for l in range(k):  # 前一天剩余的食物数
            for j in range(k):  # 今天剩余的食物数
                if 0 <= j - l + 1 <= k:  # 判断购买是否合法
                    dp[i][j] = min(dp[i][j], dp[i - 1][l] + (j - l + 1) * data[i - 1])
    
    # 返回答案:第 n 天,背包空的最小花费
    return dp[n][0]

# 测试样例
if __name__ == "__main__":
    print(solution(5, 2, [1, 2, 3, 3, 2]) == 9)
    print(solution(6, 3, [4, 1, 5, 2, 1, 3]) == 9)
    print(solution(4, 1, [3, 2, 4, 1]) == 10)

思路分析与总结

时间复杂度

动态规划的三重循环结构:

  1. O(n) 遍历天数。
  2. O(k) 遍历前一天的食物剩余量。
  3. O(k) 遍历当天的食物剩余量。

总体时间复杂度为 O(n * k^2)

空间复杂度

使用了一个二维数组 dp,空间复杂度为 O(n * k)

代码优化思考

目前的实现效率适中,但在 k 值较大时可能会造成性能瓶颈:

  1. 状态转移优化:可以使用滚动数组优化空间复杂度,从 O(n * k) 降低到 O(k)
  2. 单调队列优化:在循环中,可以通过单调队列加速对最优状态的转移,进一步降低时间复杂度。