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

101 阅读4分钟

def solution(n, k, data): dp = [[float('inf')] * (k + 1) for _ in range(n + 1)] dp[0][0] = 0

for i in range(1, n + 1):
    for l in range(k):
        for j in range(k):
            if l - j + 1 >= 0 and l - j + 1 <= k:
                dp[i][l] = min(dp[i][l], dp[i - 1][j] + (l - j + 1) * data[i - 1])
# print(dp)
return dp[n][0]

if name == "main": print(solution(5, 2, [1, 2, 3, 3, 2]) == 9) print(solution(6, 3, [4, 1, 5, 2, 1, 3]) == 9) print(solution(4, 1, [3, 2, 4, 1]) == 10)

solution 函数试图通过动态规划解决一个优化问题。它的目标是给定参数 ( n )(元素数量)、( k )(一个特定的限制或条件)和 data(一个成本或权重数组),最小化某种成本。

动态规划表

  1. 初始化

    • 函数初始化了一个大小为 (n + 1) x (k + 1) 的二维列表 dp,用 float('inf') 填充,表示所有状态最开始都被认为是不可达或成本无穷大。
    • 初始点 dp[0][0] 设置为 0,意味着在没有选择任何元素的情况下,成本为 0
  2. 填充 DP 表

    • 外层循环遍历前 ( n ) 个元素。
    • 中间循环遍历考虑的元素数量(最大为 ( k ))。
    • 最内层循环遍历前一个状态的数量 ( j ),计算将当前元素(由 data[i - 1] 表示)纳入考虑是否会使当前状态 ( l ) 的成本更低。
    • 特别地,检查从 ( j ) 到 ( l ) 的变换是否合法,变换受 ( (l - j + 1) ) 的影响,再乘以当前元素的成本。
    • 这个条件确保了 dp[i][l] 的更新考虑了所有有效的从 dp[i-1][j] 的转移。
  3. 返回值

    • 最后,函数返回 dp[n][0],表示在考虑所有元素后,能够实现的最小成本。

示例输出

if __name__ == "__main__": 部分,代码测试了函数的几个示例,以验证其功能:

  • solution(5, 2, [1, 2, 3, 3, 2]) 应返回 9
  • solution(6, 3, [4, 1, 5, 2, 1, 3]) 应返回 9
  • solution(4, 1, [3, 2, 4, 1]) 应返回 10 让我们更详细地分析这段代码的逻辑,并补充背景信息,以帮助理解动态规划(Dynamic Programming,DP)在这个上下文中的应用。

代码逻辑详解

1. 动态规划表的初始化

dp = [[float('inf')] * (k + 1) for _ in range(n + 1)]
dp[0][0] = 0
  • 创建一个大小为 (n + 1) x (k + 1) 的二维数组 dp,其中 dp[i][l] 表示在考虑前 i 个元素时,如果目标是形成某种状态 l,所需的最小成本。
  • 将所有的初始值设置为正无穷 (float('inf')),表示这些状态一开始是不可达的。
  • 设置 dp[0][0] = 0,表示在没有处理任何元素(即 i = 0)和没有形成任何状态(即 l = 0)时,所需成本为 0

2. 填充动态规划表

接下来是填充动态规划表的过程。这一部分的核心逻辑可以分解如下:

for i in range(1, n + 1):
    for l in range(k):
        for j in range(k):
            if l - j + 1 >= 0 and l - j + 1 <= k:
                dp[i][l] = min(dp[i][l], dp[i - 1][j] + (l - j + 1) * data[i - 1])
  • 外层循环 (for i in range(1, n + 1)):遍历所有数据元素,从 1n
  • 中间循环 (for l in range(k)):考虑目标状态 l 的所有可能值(从 0k-1)。
  • 内层循环 (for j in range(k)):遍历状态 j,它是前一个元素的状态,用来计算当前状态转移的成本。
条件判断和状态转移
  • if l - j + 1 >= 0 and l - j + 1 <= k:确保从状态 j 转移到状态 l 是有效的。其中 l - j + 1 表示从前一个状态 j 到当前状态 l 的变化量。
  • 成本的计算为 dp[i - 1][j] + (l - j + 1) * data[i - 1]
    • dp[i - 1][j] 是在处理前 i-1 个元素时形成状态 j 的最小成本。
    • (l - j + 1) * data[i - 1] 是将当前元素 data[i - 1] 纳入考虑所带来的额外成本,变化量再乘以当前元素的权重。
  • 使用 min(...) 更新当前状态 dp[i][l],保证保存最小成本。

3. 最终结果

return dp[n][0]
  • 函数最终返回 dp[n][0],表示在考虑所有 n 个元素后,实现目标状态 0 的最小成本。这是算法的终点,意味着我们希望以最小的成本达到一种称为状态 0 的结果。

示例输出分析

在测试部分中,分别调用 solution 函数并对输出进行验证,目的在于确保实现的动态规划算法正确无误。

if __name__ == "__main__":
    print(solution(5, 2, [1, 2, 3, 3, 2]) == 9) # 应返回 True
    print(solution(6, 3, [4, 1, 5, 2, 1, 3]) == 9) # 应返回 True
    print(solution(4, 1, [3, 2, 4, 1]) == 10) # 应返回 True

这些示例测试了不同的数据输入,以验证最终计算出的最小成本是否符合预期。

总结

这段代码有效地运用了动态规划技术来处理一个优化问题,核心在于通过构建状态转移表 dp,逐步考虑所有可能的选择来找到最小成本。动态规划的本质是在于通过“小问题”的解来逐步构建“大问题”的解,从而使计算过程更加高效。