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

163 阅读5分钟

问题描述

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

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


测试样例

样例1:

输入:n = 5 ,k = 2 ,data = [1, 2, 3, 3, 2]
输出:9

样例2:

输入:n = 6 ,k = 3 ,data = [4, 1, 5, 2, 1, 3]
输出:9

样例3:

输入:n = 4 ,k = 1 ,data = [3, 2, 4, 1]
输出:10

解题思路

首先精简题目:小R每天都需要花费一份食物,到达终点的过程是遍历 数组的过程,存在最多携带食物k。 面对有限制的最优问题考虑使用动态规划dp解决。

1. 导入必要的模块

import math

这里导入了 math 模块,主要是为了使用其中的 math.inf(正无穷大)来初始化动态规划数组中的元素,表示初始的不可达状态或者极大的花费值。

2. 定义函数 solution

def solution(n, k, data):
  • n:表示徒步旅行的总天数,也就是总共要经过的补给站数量(因为每天经过一个补给站)。
  • k:小 R 最多能同时携带的食物份数。
  • data:一个列表,其中 data[i] 表示第 i 个补给站每份食物的价格。

3. 初始化动态规划数组 dp

dp = [[math.inf for _ in range(k)]for _ in range(n+1)]
dp[0][0] = 0
  • 首先创建了一个二维列表 dp,它的维度是 (n + 1) × k。这里 dp[i][j] 表示在第 i 天(经过第 i 个补给站)且携带 j 份食物的情况下,完成后续旅程的最低花费。初始时,将所有元素都设为 math.inf,表示这些状态的花费还未确定且初始认为是不可达的(极大花费)。然后将 dp[0][0] 设为 0,因为在旅行开始前(第 0 天),没有经过任何补给站且携带 0 份食物时,花费自然是 0

4. 动态规划计算过程

for  i in range(n):
    for j in range(k):       
        for m in range(k-j+1):
            if j - 1 <0 and m == 0:
                continue
            else:
                dp[i+1][j-1+m] = min([dp[i+1][j-1+m],dp[i][j] + m*data[i]])
  • 这是一个三重循环也是程序的逻辑核心,用于填充动态规划数组 dp

    • 外层循环 for i in range(n):遍历每一天(每个补给站),从第 0 天到第 n - 1 天。
    • 中层循环 for j in range(k):对于每一天,遍历当前可能携带的食物份数,从 0 到 k - 1 份。
    • 内层循环 for m in range(k-j+1):对于每一天且携带 j 份食物的情况,遍历在当前补给站可能购买的食物份数 m。这里 k - j + 1 的计算是因为最多能携带 k 份食物,已经有 j 份了,所以最多还能购买 k - j 份,再加上可以不购买(即 m = 0)的情况,所以范围是 0 到 k - j + 1
  • 在循环内部:

    • 首先有一个条件判断 if j - 1 <0 and m == 0:,如果当前携带的食物份数 j 为 0 且又不打算在当前补给站购买食物(m = 0),那么这种情况是无法继续旅行的(因为每天都需要消耗一份食物),所以直接跳过这次循环,不进行后续的计算。
    • 否则,就进行状态转移的计算。dp[i+1][j-1+m] 表示在第 i + 1 天(下一个补给站)且携带 j - 1 + m 份食物的情况下的最低花费。通过 min([dp[i+1][j-1+m],dp[i][j] + m*data[i]]) 来更新这个值,即取当前已经计算出的 dp[i+1][j-1+m](可能之前在其他路径下已经有了一个值)和新计算出的花费值 dp[i][j] + m*data[i]dp[i][j] 是在前一天且携带 j 份食物的最低花费,m*data[i] 是在当前补给站购买 m 份食物的花费)中的最小值,作为 dp[i+1][j-1+m] 的新值。

5. 返回结果

return min(dp[-1])

在完成动态规划数组 dp 的填充后,最后一天(第 n 天)的各种携带食物份数情况下的最低花费都已经计算出来了,存储在 dp[-1](即 dp[n])这一行中。通过 min(dp[-1]) 取出这一行中的最小值,就是完成整个徒步旅行的最低花费。

总结

  • 这段代码通过动态规划的思想解决了小 R 徒步旅行中以最小花费获取食物的问题。
  • 首先初始化了一个二维动态规划数组 dp,用于存储在不同天数和不同携带食物份数情况下的最低花费。
  • 然后通过三重循环遍历每一天、每一种当前携带食物份数以及每一种在当前补给站可能购买的食物份数,根据条件判断和状态转移方程来更新 dp 数组的值。
  • 最后从最后一天的所有可能情况中取出最低花费作为整个徒步旅行的最低花费。通过这种方式,有效地利用了动态规划的优势,避免了重复计算,从而高效地解决了给定问题。