解题思路
1. 问题分析
小U需要每天一份食物,总共需要 M 天,并通过一些补给站购买食物。每个补给站位于特定的天数,且每份食物的价格各不相同。目的是规划购买策略,使得总花费最小。这是一个典型的最优子结构问题,动态规划(DP)是最优选择
2. 动态规划的思路
设 dp[i] 表示到第 i 天所需的最小花费:
-
初始状态:
dp[0] = 0,表示第0天的花费为0
-
状态转移方程:
- 从
j天购买若干天食物至i天,所需费用为dp[j] + price[j] * (i - j) - 状态转移公式为: dp[i]=min(dp[i],dp[j]+price[j]×(i−j))(j<i 且补给站存在)
- 从
-
结果:
- 返回
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)
代码详解
-
初始化:
dp = [float('inf')] * (m + 1),表示每一天的初始花费设为无穷大dp[0] = 0,因为第0天花费为0
-
价格数组填充:
- 遍历
p数组,将补给站的价格填入price数组
- 遍历
-
动态规划递推:
- 遍历每一天
i,对于每一天j:- 如果第
j天有补给站,尝试更新第i天的最小花费 - 转移方程:
dp[i] = min(dp[i], dp[j] + price[j] * (i - j))
- 如果第
- 遍历每一天
-
结果返回:
dp[m]为第M天的最小花费
难点分析
-
价格信息的存储:
price数组需要按天存储价格,确保能够快速访问和计算
-
动态规划的状态转移:
- 需要仔细考虑如何从第
j天购买到第i天,保证费用计算正确
- 需要仔细考虑如何从第
-
边界条件处理:
- 确保补给站存在 (
price[j] > 0) 才能进行购买操作
- 确保补给站存在 (
复杂度分析
-
时间复杂度:
- 外层循环:从第1天遍历到第M天,复杂度为 O(M)
- 内层循环:从第0天到第i天,复杂度为 O(M)
- 总复杂度:O(M^2)
-
空间复杂度:
- 动态规划数组
dp和价格数组price,空间复杂度为 O(M)
- 动态规划数组