补给站最优花费问题
题目描述
问题分析
这道题的本质可以总结为动态规划中的 路径优化问题,具体属于 区间型动态规划问题,其中涉及到的是在一条路径(时间或距离)上,如何最小化某种成本(这里是食物的总花费)。 路径优化的分类可以不加以深入思考,我们的解法参考背包问题,要找到一条路径上的最优解。
为什么归类为区间型动态规划?
-
区间特性:
- 每个补给站影响的是一个区间(即从
a[j]到当前天i)。 - 为了确保每天都有足够的食物,我们需要从某一天的补给站为后续区间提供足够的资源。
- 每个补给站影响的是一个区间(即从
-
决策方式:
- 对于每一天的
dp[i],我们需要考虑从之前的多个补给站进行决策,选择最优的补给站。 - 这需要遍历所有可能的区间起点(即
a[j])。
- 对于每一天的
和其他动态规划问题的区别
-
与背包问题的区别:
- 背包问题是选取有限的物品放入容量有限的背包以最大化价值,而这里是选取补给站以满足每天的食物需求,目标是最小化花费。
-
与线性路径问题的区别:
- 本题路径上存在多个补给站,每个补给站可以供给多个天数,这增加了区间选择的复杂度。
-
与区间型 DP 的联系:
- 本题的动态规划数组
dp本质上记录了从起点到某天的最小花费,状态转移时需要枚举多个区间的决策,因此具有典型的区间特性。
- 本题的动态规划数组
动态规划状态分析
-
状态定义:
dp[i]表示从起点到第i天的最小总花费。
-
转移方程:
- 对于每一个补给站
j,如果a[j] < i,可以从补给站j购买食物满足从a[j]到i天的需求:dp[i]=min(dp[i],dp[a[j]]+(i−a[j])×b[j])
- 对于每一个补给站
-
边界条件:
dp[0] = 0表示第 0 天(起点)无花费。
-
目标:
- 计算
dp[m],即最后一天的最小花费。
- 计算
Python代码
def solution(m: int, n: int, p: list[list[int]]) -> int:
# Edit your code here
# 初始化a和b数组
a = [pair[0] for pair in p]
b = [pair[1] for pair in p]
# 初始化dp数组,dp[i] 表示到第 i 天为止的最小花费
dp = [float('inf')] * (m + 1)
dp[0] = 0
# 第 0 天的花费为 0(起点)
# 遍历每一天,计算最小花费
for i in range(1, m + 1):
# 尝试从之前的每个补给站购买足够的食物
for j in range(n):
if a[j] < i: # 如果补给站在当前天数之前
# 从第 a[j] 天的补给站买 (i - a[j]) 份食物
dp[i] = min(dp[i], dp[a[j]] + (i - a[j]) * b[j])
# 返回在第 m 天结束时的最小花费
return dp[m]
if __name__ == "__main__":
# Add your test cases here
print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]]) == 7)