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

109 阅读2分钟

解题思路

1. 问题分析

小U需要每天一份食物,总共需要 M 天,并通过一些补给站购买食物。每个补给站位于特定的天数,且每份食物的价格各不相同。目的是规划购买策略,使得总花费最小。这是一个典型的最优子结构问题,动态规划(DP)是最优选择

2. 动态规划的思路

dp[i] 表示到第 i 天所需的最小花费:

  1. 初始状态:

    • dp[0] = 0,表示第0天的花费为0
  2. 状态转移方程:

    • j 天购买若干天食物至 i 天,所需费用为 dp[j] + price[j] * (i - j)
    • 状态转移公式为: dp[i]=min⁡(dp[i],dp[j]+price[j]×(i−j))(j<i 且补给站存在)
  3. 结果:

    • 返回 dp[M],即完成旅行所需的最小花费。

代码实现

def solution(m: int, n: int, p: list[list[int]]) -> int:
    # 初始化动态规划数组 dp,每天的花费初始化为无穷大
    dp = [float('inf')] * (m + 1)
    dp[0] = 0  # 第0天的费用为0
    
    # 初始化价格数组 price,用于存储每一天的食物价格
    price = [0] * (m + 1)
    
    # 填充补给站的价格信息到 price 数组
    for A, B in p:
        price[A] = B  # 第 A 天的食物价格为 B
    
    # 动态规划:计算从第 1 天到第 M 天的最小花费
    for i in range(1, m + 1):
        for j in range(i):
            if price[j] > 0:  # 第 j 天有补给站
                # 从第 j 天购买若干天食物至第 i 天
                dp[i] = min(dp[i], dp[j] + price[j] * (i - j))

    # 返回第 M 天的最小花费
    return dp[m]

# 测试用例
if __name__ == "__main__":
    print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]]) == 7)

代码详解

  1. 初始化:

    • dp = [float('inf')] * (m + 1),表示每一天的初始花费设为无穷大
    • dp[0] = 0,因为第0天花费为0
  2. 价格数组填充:

    • 遍历 p 数组,将补给站的价格填入 price 数组
  3. 动态规划递推:

    • 遍历每一天 i,对于每一天 j
      • 如果第 j 天有补给站,尝试更新第 i 天的最小花费
      • 转移方程:dp[i] = min(dp[i], dp[j] + price[j] * (i - j))
  4. 结果返回:

    • dp[m] 为第 M 天的最小花费

难点分析

  1. 价格信息的存储:

    • price 数组需要按天存储价格,确保能够快速访问和计算
  2. 动态规划的状态转移:

    • 需要仔细考虑如何从第 j 天购买到第 i 天,保证费用计算正确
  3. 边界条件处理:

    • 确保补给站存在 (price[j] > 0) 才能进行购买操作

复杂度分析

  1. 时间复杂度:

    • 外层循环:从第1天遍历到第M天,复杂度为 O(M)
    • 内层循环:从第0天到第i天,复杂度为 O(M)
    • 总复杂度:O(M^2)
  2. 空间复杂度:

    • 动态规划数组 dp 和价格数组 price,空间复杂度为 O(M)