青训营X豆包MarsCode 技术训练营刷题技巧(动态规划专题篇四) | 豆包MarsCode AI 刷题

93 阅读3分钟

补给站最优花费问题

题目描述

image.png

问题分析

这道题的本质可以总结为动态规划中的 路径优化问题,具体属于 区间型动态规划问题,其中涉及到的是在一条路径(时间或距离)上,如何最小化某种成本(这里是食物的总花费)。 路径优化的分类可以不加以深入思考,我们的解法参考背包问题,要找到一条路径上的最优解。

为什么归类为区间型动态规划?
  • 区间特性

    • 每个补给站影响的是一个区间(即从 a[j] 到当前天 i)。
    • 为了确保每天都有足够的食物,我们需要从某一天的补给站为后续区间提供足够的资源。
  • 决策方式

    • 对于每一天的 dp[i],我们需要考虑从之前的多个补给站进行决策,选择最优的补给站。
    • 这需要遍历所有可能的区间起点(即 a[j])。
和其他动态规划问题的区别
  1. 与背包问题的区别

    • 背包问题是选取有限的物品放入容量有限的背包以最大化价值,而这里是选取补给站以满足每天的食物需求,目标是最小化花费。
  2. 与线性路径问题的区别

    • 本题路径上存在多个补给站,每个补给站可以供给多个天数,这增加了区间选择的复杂度。
  3. 与区间型 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)