青训营X豆包MarsCode 技术训练营题目解析——小S的货船租赁冒险| 豆包MarsCode AI 刷题

97 阅读4分钟

题目解析

1. 问题背景

小S有一个预算 VV,希望从 QQ 种不同类型的货船中选择一些货船,使得 租赁总成本不超过预算,并且 总载货量最大化
每种货船的信息包括:

  • 数量 m[i]m[i]: 第 ii 种货船最多可以租用的数量。
  • 租赁成本 v[i]v[i]: 每艘该种货船的租赁成本。
  • 最大载货量 w[i]w[i]: 每艘该种货船的最大载货量。

这是一个经典的多重背包问题,目标是求出在预算 VV 内能够获得的最大总载货量。


2. 输入输出解析

输入:

  • QQ:货船种类数量。
  • VV:预算总额。
  • ships\text{ships}:一个二维数组,包含每种货船的信息 [m[i],v[i],w[i]][m[i], v[i], w[i]]。

输出:

  • 最大总载货量: 满足预算限制下,小S能租用货船的最大总载货量。

算法思路

1. 动态规划建模

这是一个多重背包问题,解决方法为动态规划

  1. 状态定义:

    • 使用 dp[j]dp[j] 表示在预算 jj 下,可以达到的最大总载货量。
  2. 状态转移:

    • 对于第 ii 种货船,枚举租用的数量 kk: dp[j]=max⁡(dp[j],dp[j−k⋅v[i]]+k⋅w[i])dp[j] = \max(dp[j], dp[j - k \cdot v[i]] + k \cdot w[i]) 其中 1≤k≤min⁡(m[i],⌊j/v[i]⌋)1 \leq k \leq \min(m[i], \lfloor j / v[i] \rfloor),表示不能超过货船数量限制,且租赁总成本不超预算。
  3. 边界条件:

    • 初始时,dp[0]=0dp[0] = 0(预算为 0 时,总载货量为 0)。
  4. 优化方向:

    • 从大到小遍历预算 jj,确保当前决策不会干扰后续决策。

2. 解题步骤

  1. 初始化一个大小为 V+1V+1 的数组 dpdp,用来记录不同预算下的最大载货量。

  2. 遍历每种货船,对每个预算 jj:

    • 枚举租用该货船的数量 kk,更新 dp[j]dp[j]。
  3. 最终,返回 dp[V]dp[V] 即为结果。


代码解析

def solution(Q, V, ships):
    # 初始化 dp 数组,dp[j] 表示预算 j 下的最大总载货量
    dp = [0] * (V + 1)
    
    # 遍历每种货船类型
    for m, v, w in ships:
        # 从高到低遍历预算,防止重复计算
        for j in range(V, -1, -1):
            # 尝试租用 k 艘货船
            for k in range(1, min(m, j // v) + 1):  # k 最大为 m 或预算能买得起的数量
                dp[j] = max(dp[j], dp[j - k * v] + k * w)
    
    # 返回在预算 V 下的最大载货量
    return dp[V]

# 测试用例
if __name__ == "__main__":
    ships1 = [[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, ships1) == 32)  # 测试结果: True
    print(solution(23, 400, ships2) == 1740)  # 测试结果: True

详细运行示例

样例 1:

输入:

Q = 2, V = 10
ships = [[2, 3, 2], [3, 2, 10]]
  1. 初始 dp=[0,0,0,…,0]dp = [0, 0, 0, \dots, 0](长度为 V+1=11V+1 = 11)。

  2. 遍历第 1 种货船(2 艘,价格 3,载货量 2):

    • j=10j = 10 至 j=0j = 0,枚举租用 kk 艘:

      • k=1k = 1: dp[10]=max⁡(dp[10],dp[7]+2)dp[10] = \max(dp[10], dp[7] + 2)。
      • k=2k = 2: dp[10]=max⁡(dp[10],dp[4]+4)dp[10] = \max(dp[10], dp[4] + 4)。
  3. 遍历第 2 种货船(3 艘,价格 2,载货量 10):

    • 类似更新 dpdp,最终得到 dp[10]=32dp[10] = 32。

输出:32


复杂度分析

  1. 时间复杂度:

    • 外层循环 QQ 次(货船种类)。
    • 每次遍历预算 VV,枚举货船数量 kk,复杂度为 O(Q⋅V⋅K)O(Q \cdot V \cdot K),其中 KK 是单种货船的最大租用数量。
  2. 空间复杂度:

    • O(V)O(V) 用于存储 dpdp 数组。

优化可以采用二进制分组优化枚举 kk,减少复杂度到 O(Q⋅V⋅log⁡M)O(Q \cdot V \cdot \log M),其中 MM 是单种货船最大数量。