第七篇|小S的货船租赁冒险|豆包MarsCode AI技术刷题

303 阅读7分钟

一、问题描述

小S在码头租用货船,有 Q 种不同类型的货船可供选择。每种货船有固定的数量 m[i]、租赁成本 v[i] 和最大载货量 w[i]。小S希望在预算 V 元内,租用能够承载最大总货物的货船组合。每种货船的具体信息包括数量、租赁价格和载货量。小S需要你帮忙计算在给定预算下,她能租用的货船的最大总载货量是多少。

  • Q: 货船的种类数量。
  • V: 李华可用的总预算(单位:元)。
  • ships: 一个列表,其中每个元素是一个元组 [m[i], v[i], w[i]],分别表示第 i 种货船的数量、租赁价格和每艘货船的最大载货量。

测试样例

样例1:

输入:Q = 2,V = 10,ships = [[2, 3, 2], [3, 2, 10]]
输出:32

样例2:

输入:Q = 3,V = 50,ships = [[5, 10, 20], [2, 20, 30], [3, 15, 25]]
输出:100

样例3:

输入:Q = 1,V = 100,ships = [[10, 5, 50]]
输出:500

样例4:

输入:Q = 4,V = 100,ships = [[1, 100, 200], [2, 50, 100], [3, 33, 66], [4, 25, 50]]
输出:200

样例5:

输入:Q = 2,V = 300,ships = [[100, 1, 1], [50, 5, 10]]
输出:550

二、代码思路

def solution(Q, V, ships):
    dp = [[0] * (V + 1) for _ in range(Q + 1)]

    for i in range(1, Q + 1):
        for j in range(1, V + 1):
            for k in range(min(ships[i - 1][0], j // ships[i - 1][1]) + 1):
                dp[i][j] = max(dp[i][j], dp[i - 1][j - k * ships[i - 1][1]] + k * ships[i - 1][2])

    return dp[Q][V]

if __name__ == "__main__":
    #  You can add more test cases here
    ships = [[2, 3, 2], [3, 2, 10]]
    ships2 = [[30, 141, 47], [9, 258, 12], [81, 149, 13], [91, 236, 6], [27, 163, 74], [34, 13, 58], [61, 162, 1], [80, 238, 29], [36, 264, 28], [36, 250, 2], [70, 214, 31], [39, 116, 39], [83, 287, 4], [61, 269, 94], [23, 187, 46], [78, 33, 29], [46, 151, 2], [71, 249, 1], [67, 76, 85], [72, 239, 17], [61, 256, 49], [48, 216, 73], [39, 49, 74]]
    print(solution(2, 10, ships) == 32)
    print(solution(23, 400, ships2) == 1740)

以下是对上述代码的详细分析:

整体功能

这段代码实现了一个通过动态规划解决特定组合优化问题的功能。具体来说,它似乎是在给定一些船只相关的参数以及容量限制的情况下,计算能获得的最大收益(或者其他可量化的最优值,这里根据代码中的计算逻辑推测可能与船只运输相关的某种最优结果)。

代码结构

1. 初始化动态规划数组:
  dp = [[0] * (V + 1) for _ in range(Q + 1)]

这里创建了一个二维数组  dp ,用于存储中间计算结果。其中  Q  和  V  分别是问题中的两个维度相关的参数(可能是船只数量和某种容量限制等), dp[i][j]  将用于表示在考虑前  i  个船只且总容量为  j  的情况下的最优解。

2. 动态规划计算循环:
for i in range(1, Q + 1):
    for j in range(1, V + 1):
        for k in range(min(ships[i - 1][0], j // ships[i - 1][1]) + 1):
            dp[i][j] = max(dp[i][j], dp[i - 1][j - k * ships[i - 1][1]] + k * ships[i - 1][2])
  • 外层的两个循环  for i in range(1, Q + 1)  和  for j in range(1, V + 1)  遍历了所有可能的船只数量和容量组合情况。对于每一种组合,都要通过内层循环来计算当前情况下的最优值。
  • 内层循环  for k in range(min(ships[i - 1][0], j // ships[i - 1][1]) + 1)  这里的  k  可能表示选择当前第  i  个船只的数量。循环的范围是通过取船只可选择的最大数量(由  ships[i - 1][0]  表示)和在当前容量  j  下能容纳的该船只最大数量(由  j // ships[i - 1][1]  计算得到)中的较小值再加1来确定。
  • 在每次内层循环中,通过  dp[i][j] = max(dp[i][j], dp[i - 1][j - k * ships[i - 1][1]] + k * ships[i - 1][2])  来更新  dp[i][j]  的值。它的含义是在当前考虑前  i  个船只且总容量为  j  的情况下,比较当前已有的  dp[i][j]  值和通过选择  k  个第  i  个船只所得到的新的可能最优值(通过  dp[i - 1][j - k * ships[i - 1][1]] + k * ships[i - 1][2]  计算,其中  dp[i - 1][j - k * ships[i - 1][1]]  表示在前  i - 1  个船只且剩余容量为  j - k * ships[i - 1][1]  时的最优解, k * ships[i - 1][2]  表示选择  k  个第  i  个船只所带来的收益或其他相关值),取两者中的最大值作为新的  dp[i][j]  值。
3. 返回最终结果:
  return dp[Q][V]

在完成所有的动态规划计算后,返回  dp[Q][V] ,即考虑了所有  Q  个船只且总容量为  V  时的最优解。

4.主函数部分
if __name__ == "__main__":
    #  You can add more test cases here
    ships = [[2, 3, 2], [3, 2, 10]]
    ships2 = [[30, 141, 47], [9, 258, 12], [81, 149, 13], [91, 236, 6], [27, 163, 74], [34, 13, 58], [61, 162, 1], [80, 238, 29], [36, 264, 28], [36, 250, 2], [70, 214, 31], [39, 116, 39], [83, 287, 4], [61, 269, 94], [23, 187, 46], [78, 33, 29], [46, 151, 2], [71, 249, 1], [67, 76, 85], [72, 239, 17], [61, 256, 49], [48, 216, 73], [39, 49, 74]]
    print(solution(2, 10, ships) == 32)
    print(solution(23, 400, ships2) == 1740)

 在主函数部分,定义了两组船只数据  ships  和  ships2 ,然后分别调用  solution  函数并通过比较返回结果与预期结果(这里是硬编码的  32  和  1740 )是否相等来进行简单的测试,以验证  solution  函数在给定不同输入情况下是否能正确计算出预期的最优解。

总体而言,这段代码通过动态规划的思想有效地解决了一个与船只相关参数及容量限制有关的组合优化问题,但代码中的变量命名相对不够清晰,如果能使用更具描述性的变量名,代码的可读性会更好。

通过AI提示优化之后

def solution(Q, V, ships):
    # 将每种货船的数量拆分成多个独立的物品
    items = []
    for m, v, w in ships:
        k = 1
        while m > 0:
            if m >= k:
                items.append((k * v, k * w))
                m -= k
                k *= 2
            else:
                items.append((m * v, m * w))
                m = 0

    # 使用一维数组进行动态规划
    dp = [0] * (V + 1)
    for v, w in items:
        for j in range(V, v - 1, -1):
            dp[j] = max(dp[j], dp[j - v] + w)

    return dp[V]

if __name__ == "__main__":
    #  You can add more test cases here
    ships = [[2, 3, 2], [3, 2, 10]]
    ships2 = [[30, 141, 47], [9, 258, 12], [81, 149, 13], [91, 236, 6], [27, 163, 74], [34, 13, 58], [61, 162, 1], [80, 238, 29], [36, 264, 28], [36, 250, 2], [70, 214, 31], [39, 116, 39], [83, 287, 4], [61, 269, 94], [23, 187, 46], [78, 33, 29], [46, 151, 2], [71, 249, 1], [67, 76, 85], [72, 239, 17], [61, 256, 49], [48, 216, 73], [39, 49, 74]]
    print(solution(2, 10, ships) == 32)
    print(solution(23, 400, ships2) == 1740)

解释

  1. 二进制优化

    • 将每种货船的数量 m 拆分成多个独立的物品,每个物品的重量和价值是原物品的倍数。例如,如果 m = 5,则可以拆分成 1, 2, 2 三个物品。
  2. 一维数组优化

    • 使用一维数组 dp 进行动态规划,减少了空间复杂度。
  3. 状态转移方程

  • dp[j] = max(dp[j], dp[j - v] + w),表示在考虑当前物品时,选择是否将其加入背包。