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

34 阅读2分钟

问题文本

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

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

问题分析

该题目可以用动态规划的方法解决。以下是该题目的限制:

  1. 小R必须保证到下一天的时候身上至少有一份食物,这限制了小R每天可购买食物数量的最小值。
  2. 小R身上最多携带K份食物,这限制了小R每天可购买食物数量的最大值。
  3. 考虑到最后结余食物的情况一定会比刚好消耗完食物的情况花费高,所以可以认为在最后一天时,小R身上应该只有一份食物,这同样是小R每天可购买食物数量的最大值限制。

因为小R身上可以存储食物,所以一个很直接的想法是我们在价格便宜的日子尽可能地多买以避免在价格贵的日子购买食物,因此小R每次购买食物应当都是在食物耗尽时购买。我们用fif_i表示小R在第i天购买的食物数量,假设小R在第j天购买了食物,在第i天未耗尽食物的情况下继续购买了食物。

若price[j]<pricep[i],则小R在第i天购买的食物可以以更低的价格在第j天购买,即

price[j](fi+fj)<price[i]fi+price[j]fjprice[j] * (f_i + f_j) < price[i] * f_i + price[j] * f_j

若price[j]>price[i],则小R在第j天购买刚好支撑到第i天数量的食物价格更低,即

price[i](fi+fj(ij))+price[j](ij)<price[i]fi+price[j]fjprice[i] * (f_i + f_j - (i - j)) + price[j] * (i - j) < price[i] * f_i + price[j] * f_j

我们用长度为n的数组dp存储小R到每天的最低花费,dp[i]表示小R徒步旅行到第i天的最低花费,有如下状态转移方程:

dp[i]=min(dp[i],dp[j]+(ij)price[j]i[1,n],j[max(0,ik),min(i,n1))dp[i] = min(dp[i], dp[j] + (i - j) * price[j] \quad\quad i\in[1,n],j \in [max(0,i-k), min(i, n-1))

其中 (i - j) * price[j]保证了小R每天均有食物且到第i天刚好消耗完所有食物,满足限制1,3。对于限制2,由于小R最多携带K份食物,所以对于第i天,最早仅可往前推k天:当i<k时,j的最小值为0,当i>k,j的最小值为i-k。

同时由于小R在最后一天仅会携带之前购买的一份食物,不会购买最后一天的食物,所以j的取值不会大于i和n-1。

代码实现如下:

def solution(n, k, data):

    dp = [max(data) * n] * (n + 1)

    dp[0] = 0

    for i in range(1, n + 1):
        minValue = max(0, i - k)
        maxValue = min(i, n - 1)
        for j in range(minValue, maxValue):
            dp[i] = min(dp[i], dp[j] + data[j] * (i - j))

    return dp[-1]