补给站最优花费问题 | 豆包MarsCode AI刷题

38 阅读3分钟

具体描述

image.png

代码

def solution(n, k, p):
    dp = [float('inf')] * (n + 1)
    dp[0] = 0
    for day, price in p:
        for j in range(n, day - 1, -1):
            dp[j] = min(dp[j], dp[j - day] + price * day)
    return dp[n]


if __name__ == "__main__":
    print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]]) == 7)
    print(solution(6, 5, [[0, 1], [1, 5], [2, 2], [3, 4], [5, 1]]) == 6)
    print(solution(4, 3, [[0, 3], [2, 2], [3, 1]]) == 9)

思路

问题的目标是确保小U在徒步旅行中每天都有食物,同时花费最少的钱购买食物。为了达到这个目标,我们需要合理规划在不同补给站的购买策略。

关键挑战是:

  1. 确保在每一天都有食物。
  2. 优先在便宜的补给站购买食物,但要考虑补给站的分布和到达的天数。

解法分析

  • 动态规划思想: 我们使用动态规划来解决这个问题。定义 dp[i] 表示在第 i 天拥有足够食物所需的最小花费。

  • 状态转移方程:

    • 对于每个补给站 (day, price),表示可以从第 day 天开始购买食物,我们考虑从前面的天数转移到第 day 天并补充食物。
    • 如果在第 day 天购买食物,更新所有可能的 dp[j]
  • 初始化:

    • dp[0] = 0 表示第 0 天不需要任何花费。
    • 其他所有 dp[i] 初始化为无穷大。

分步解析

  • 初始化动态规划数组 dp
dp = [float('inf')] * (n + 1)
dp[0] = 0
  • 遍历每个补给站,从最后一天开始倒序更新 dp

  • 更新状态转移方程,计算最小花费。

    for day, price in p:
        for j in range(n, day - 1, -1):
            dp[j] = min(dp[j], dp[j - day] + price * day)

关键点

  1. 动态规划核心思想: 使用 dp 数组记录到达每一天所需的最小花费,通过遍历补给站更新 dp
  2. 倒序遍历: 遍历天数时倒序处理,确保更新时不会影响前面的状态。
  3. 灵活处理边界情况: 确保从第 0 天开始有食物,且补给站按顺序给定。

时间复杂度

  • O(N × M): 其中 N 是补给站数量,M 是总天数。每个补给站需要遍历所有可能的天数,整体复杂度较高但可接受。

总结

这道题是一个经典的动态规划问题,涉及到最优路径和资源分配。动态规划适合解决具有重叠子问题和最优子结构的问题。在这道题中,每一天的最小花费依赖于前面的天数是否能以最小代价覆盖,因此我们需要通过递推的方式逐步更新每一天的花费。

在动态规划中,倒序遍历可以避免状态的重复覆盖。在更新 dp 数组时,从最后一天开始往前处理,可以确保我们在更新某一天的花费时,不会影响前面已经计算好的状态。这个技巧在背包问题等经典问题中也非常常见。