2.徒步旅行中的补给问题|豆包MarsCode AI刷题

90 阅读3分钟

问题描述

小R正在计划一次从地点A到地点B的徒步旅行,总路程需要 N 天。为了在旅途中保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R在路途中每天都会经过一个补给站,可以购买食物进行补充。然而,每个补给站的食物每份的价格可能不同,并且小R最多只能同时携带 K 份食物。

现在,小R希望在保证每天都有食物的前提下,以最小的花费完成这次徒步旅行。你能帮助小R计算出最低的花费是多少吗?

动态规划思路

二维DP

状态定义

设 dpi\text{dp}idpi 表示在第 iii 天结束时,剩余 jjj 份食物的情况下的最小花费。

  • iii 表示天数(从 1 到 NNN)。
  • jjj 表示剩余的食物份数(从 0 到 KKK)。

状态转移

  1. 每天需要消耗 1 份食物,因此从 dpi−1\text{dp}i-1dpi−1 转移到 dpi\text{dp}idpi
  2. 如果在当天补给站购买 xxx 份食物(0≤x≤K−j0 \leq x \leq K - j0≤x≤K−j),需要额外支付 x⋅costx \cdot \text{cost}x⋅cost,且转移自 dpi−1\text{dp}i-1dpi−1

公式如下:

dpi=min⁡(dpi,dpi−1+x⋅cost)\text{dp}i = \min(\text{dp}i, \text{dp}i-1 + x \cdot \text{cost})dpi=min(dpi,dpi−1+x⋅cost)

其中 xxx 为当天购买的食物数量。

边界条件

  • dp0=0\text{dp}0 = 0dp0=0 (起点,无花费)。
  • 任何状态 dpi\text{dp}idpi 如果不满足食物需求,则设为无穷大。

一维DP

状态定义

  • dp[i]\text{dp}[i]dp[i]: 到达第 iii 天的最小花费。

初始化

  • dp[0]=data[0]\text{dp}[0] = \text{data}[0]dp[0]=data[0]: 第一天的花费等于第 0 天的食物价格。

转移方程

代码尝试通过以下两种方式更新 dp[i]\text{dp}[i]dp[i]:

  1. 不从过去的天数额外购买食物,仅使用前一天的花费转移:

    dp[i]=dp[i−1]+data[i]\text{dp}[i] = \text{dp}[i-1] + \text{data}[i]dp[i]=dp[i−1]+data[i]

    表示直接从第 i−1i-1i−1 天走到第 iii 天,并支付当前天的食物价格。

  2. 考虑从过去的 kkk 天中购买的食物来覆盖当天的需求:

    dp[i]=min⁡(dp[i],dp[j]+data[j])\text{dp}[i] = \min(\text{dp}[i], \text{dp}[j] + \text{data}[j])dp[i]=min(dp[i],dp[j]+data[j])

    其中 jjj 的范围是 [max⁡(0,i−k+1),i)[\max(0, i - k + 1), i)[max(0,i−k+1),i)。它尝试找出从过去 kkk 天内购买食物能否更便宜。

最终结果

  • dp[−1]\text{dp}[-1]dp[−1]: 到达第 n−1n-1n−1 天的最小花费。

一维dp代码:

def solution(n, k, data):
    dp = [float('inf')] * n
    dp[0] = data[0]
​
    for i in range(1, n):
        dp[i] = dp[i-1] + data[i]
        for j in range(max(0, i - k + 1), i):
            dp[i] = min(dp[i], dp[i-1] + data[j])
​
    return dp[-1]