AI刷题中档题旅行补给问题 解答| 豆包MarsCode AI 刷题

59 阅读5分钟

徒步旅行中的补给问题

问题背景

小R计划进行一次为期 NNN 天的徒步旅行。在旅行过程中,小R每天都需要消耗 1 份食物来维持体力。幸运的是,小R每天都会经过一个补给站,可以购买食物补充能量。然而,补给站的食物价格每天不同,同时为了方便携带和减轻负担,小R最多只能携带 KKK 份食物。为了让整个旅行的花费尽可能少,小R需要设计一个合理的购买计划。

问题目标

在保证每天都有足够食物的前提下,计算出小R完成整个旅行的最低总花费。

解题思路

这个问题的核心在于优化每日的购买和携带策略,确保花费最少的同时满足每日的食物需求。基于这一点,可以使用动态规划(Dynamic Programming,简称 DP)来解决。动态规划可以通过记录中间状态,将问题分解为更小的子问题,从而实现整体的最优解。

动态规划设计

  1. 状态定义
    我们定义一个二维 DP 表 dp[i][j]dp[i][j]dp[i][j],表示在第 iii 天结束时,小R携带 jjj 份食物所需的最低总花费。其中:

    • iii 的取值范围为 0 到 N−1N-1N−1,表示旅行的天数。
    • jjj 的取值范围为 0 到 KKK,表示当天结束时小R携带的食物数量。
  2. 状态转移方程
    第 iii 天,小R有两种选择:

    • 选择 1:不购买食物,只消耗前一天携带的食物。
      如果在第 iii 天结束时携带 jjj 份食物,则第 i−1i-1i−1 天必须至少携带 j+1j+1j+1 份食物才能支撑今天的需求。对应的状态转移公式为: dp[i][j]=min⁡(dp[i][j],dp[i−1][j+1])dp[i][j] = \min(dp[i][j], dp[i-1][j+1])dp[i][j]=min(dp[i][j],dp[i−1][j+1])
    • 选择 2:购买食物补充携带量。
      小R可以选择在第 iii 天购买 lll 份食物,并结合前一天的携带情况转移状态。如果第 iii 天结束时携带 jjj 份食物,则前一天需要携带 j−l+1j-l+1j−l+1 份,并购买 lll 份。对应的公式为: dp[i][j]=min⁡(dp[i][j],dp[i−1][j−l+1]+l×data[i])dp[i][j] = \min(dp[i][j], dp[i-1][j-l+1] + l \times \text{data}[i])dp[i][j]=min(dp[i][j],dp[i−1][j−l+1]+l×data[i]) 其中 lll 的范围为 1 到 jjj。
  3. 初始条件
    第一天 i=0i = 0i=0 时,小R必须直接购买食物,因为没有前一天的携带量。初始状态为:

    dp[0][j]=data[0]×jdp[0][j] = \text{data}[0] \times jdp[0][j]=data[0]×j

    其中 jjj 的范围为 0 到 KKK。

  4. 最终结果
    旅行结束后,第 NNN 天必须携带至少 1 份食物,才能满足需求。因此,最终答案为第 N−1N-1N−1 天携带 1 到 KKK 份食物的最小花费:

    result=min⁡(dp[N−1][1],dp[N−1][2],…,dp[N−1][K])\text{result} = \min(dp[N-1][1], dp[N-1][2], \dots, dp[N-1][K])result=min(dp[N−1][1],dp[N−1][2],…,dp[N−1][K])

算法复杂度分析

  • 时间复杂度
    每一天的 DP 状态需要遍历所有可能的携带量 KKK,并为每个状态计算所有可能的购买策略,复杂度为 O(n×k2)O(n \times k^2)O(n×k2)。

  • 空间复杂度
    需要一个大小为 n×kn \times kn×k 的 DP 表来存储状态,空间复杂度为 O(n×k)O(n \times k)O(n×k)。

    def solution(n, k, data):
        # 初始化 dp 表,表示在每一天以每种食物携带量的最小花费
        dp = [[float('inf') for _ in range(k+1)] for _ in range(n)]`
    
        # 初始化第一天的食物购买情况
        for i in range(k+1):
            dp[0][i] = data[0] * i  # 在第一天购买 `i` 份食物的花费
    
        # 动态规划填表
        for i in range(1, n):  # 遍历每一天
            for j in range(k+1):  # 遍历携带的食物数量
                # 情况1:不购买食物,从前一天带来的食物消费1份到达今天
                if j < k:
                    dp[i][j] = min(dp[i][j], dp[i-1][j+1])
    
                # 情况2:购买食物补给
                for l in range(1, j+1):
                    if j - l >= 0:
                        dp[i][j] = min(dp[i][j], dp[i-1][j-l+1] + l * data[i])
    
        # 输出 dp 表调试
        print(dp)
    

示例分析

假设 n=3n = 3n=3、k=2k = 2k=2,食物价格数组为 data=[3,2,1]\text{data} = [3, 2, 1]data=[3,2,1]。

  • 第一天:携带 0, 1, 2 份食物的花费分别为 0, 3, 6。
  • 第二天:通过动态规划,计算从第一天携带过来的食物或当天补充的食物。
  • 第三天:重复类似计算,最终取携带至少 1 份食物的最小值。

最终结果为最低花费 5。

总结

此问题通过动态规划巧妙地平衡了携带量和购买成本之间的关系,清晰地将复杂的选择过程分解为简单的子问题。设计的状态转移公式既合理又高效,可以扩展到不同的天数和携带限制。此外,算法复杂度在可接受范围内,适用于中等规模的数据输入。