具体描述
代码
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在徒步旅行中每天都有食物,同时花费最少的钱购买食物。为了达到这个目标,我们需要合理规划在不同补给站的购买策略。
关键挑战是:
- 确保在每一天都有食物。
- 优先在便宜的补给站购买食物,但要考虑补给站的分布和到达的天数。
解法分析
-
动态规划思想: 我们使用动态规划来解决这个问题。定义
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)
关键点
- 动态规划核心思想: 使用
dp数组记录到达每一天所需的最小花费,通过遍历补给站更新dp。 - 倒序遍历: 遍历天数时倒序处理,确保更新时不会影响前面的状态。
- 灵活处理边界情况: 确保从第 0 天开始有食物,且补给站按顺序给定。
时间复杂度
- O(N × M): 其中
N是补给站数量,M是总天数。每个补给站需要遍历所有可能的天数,整体复杂度较高但可接受。
总结
这道题是一个经典的动态规划问题,涉及到最优路径和资源分配。动态规划适合解决具有重叠子问题和最优子结构的问题。在这道题中,每一天的最小花费依赖于前面的天数是否能以最小代价覆盖,因此我们需要通过递推的方式逐步更新每一天的花费。
在动态规划中,倒序遍历可以避免状态的重复覆盖。在更新 dp 数组时,从最后一天开始往前处理,可以确保我们在更新某一天的花费时,不会影响前面已经计算好的状态。这个技巧在背包问题等经典问题中也非常常见。