对83.小s的货船租赁冒险解析

58 阅读5分钟

这道题目本质上是一个“01背包问题”的变种。我们需要在给定预算内选择一系列货船,以最大化可承载的货物总重量。题目中的货船不仅有数量限制,而且每种货船的租赁价格和载货量也是不同的,如何在预算限制下选择最优组合是一道典型的背包问题。

问题分析

我们可以从以下几个方面进行分析:

  1. 输入

    • Q:表示货船种类的数量。
    • V:表示总预算,限制了我们可以选择的货船组合的总费用。
    • ships:一个列表,其中每个元素是一个元组,包含三部分:
      • m[i]:第 i 种货船的数量。
      • v[i]:第 i 种货船的租赁价格。
      • w[i]:第 i 种货船的载货量。
  2. 输出

    • 需要输出在预算 V 内能够租用的货船组合能够承载的最大总货物。

背包问题的应用

这是一个典型的“0-1背包问题”,但具有一定的复杂性,因为每种货船不仅有价格和载货量,还有限定的数量。常规的背包问题每个物品只有一件,而这里每个货船种类可能有多个,且数量有限。为了应对这一点,通常有两种方法来解决:

  • 完全背包:允许多次选择某个物品。
  • 01背包:每种物品只能选择一次。

这里的货船种类是“有数量限制的物品”,可以通过“二进制拆解法”来处理。具体来说,我们可以通过将每个货船的数量分解成几个不同的“虚拟物品”来简化问题。例如,如果一种货船有10艘,我们可以将其拆成1艘、2艘、4艘、8艘这样的组合,使得每次选择的货船数量为2的幂次方。

解法思路

我们使用动态规划(Dynamic Programming, DP)来解决这个问题。假设 dp[j] 表示在预算为 j 元时,能够承载的最大货物重量。我们的目标是求出 dp[V],即在预算 V 内的最大承载重量。

  1. 初始化dp[0] = 0,表示当预算为0时,无法承载任何货物。
  2. 状态转移:对于每一种货船,我们需要更新 dp 数组。由于是有限数量的物品,我们采用“二进制拆解法”,将每种货船的数量拆解成若干个“虚拟物品”来更新 dp 数组。
  3. 二进制拆解:如果第 i 种货船有 m[i] 艘,我们可以拆成多个子问题,比如选择 1 艘、2 艘、4 艘、8 艘,依此类推。通过这种方式,可以避免每次都从头遍历所有物品,降低了复杂度。

动态规划过程详解

假设有以下货船数据:

ships = [[2, 3, 2], [3, 2, 10]]

这意味着:

  • 第一种货船有 2 艘,每艘租赁价格为 3 元,载货量为 2 吨。
  • 第二种货船有 3 艘,每艘租赁价格为 2 元,载货量为 10 吨。

我们的任务是根据预算 V 选择合适的货船组合,使得总的载货量最大。

代码实现

def solution(Q, V, ships):
    # dp[j] 表示预算为 j 时的最大载货量
    dp = [0] * (V + 1)
    
    # 遍历每种货船
    for m, cost, weight in ships:
        # 采用二进制拆解法处理每种货船
        k = 1
        while k <= m:
            # 对应选择 k 艘货船,更新 dp 数组
            for j in range(V, k * cost - 1, -1):
                dp[j] = max(dp[j], dp[j - k * cost] + k * weight)
            m -= k
            k *= 2
        # 处理剩下的 m 个货船
        if m > 0:
            for j in range(V, m * cost - 1, -1):
                dp[j] = max(dp[j], dp[j - m * cost] + m * weight)
                
    # 返回最大载货量
    return dp[V]

代码解析

  1. 初始化DP数组dp 数组用来记录在给定预算下能承载的最大载货量。dp[j] 表示预算为 j 时的最大载货量,初始时,所有的 dp[j] 都为0,表示没有租赁货船时载货量为0。

  2. 二进制拆解法:对于每种货船,首先我们通过二进制拆解法将其拆解成多个子问题。对于每个拆解出的数量(比如 1 艘、2 艘、4 艘货船),逐步更新 dp 数组。这样能够确保每种货船的数量不会超过其限制。

  3. 状态转移:每次选择了某种数量的货船后,更新 dp 数组,尝试在预算限制内获取最大载货量。

  4. 结果输出:最终,dp[V] 就是预算 V 下能够承载的最大货物总重量。

复杂度分析

  • 时间复杂度:每种货船通过二进制拆解法最多会进行 log(m[i]) 次更新,因此总的时间复杂度为 O(Q * log(m[i]) * V),其中 Q 是货船种类的数量,m[i] 是第 i 种货船的数量,V 是预算。
  • 空间复杂度:我们只使用了一个长度为 V+1dp 数组,所以空间复杂度是 O(V)

测试案例解析

  • 测试样例 1
    ships = [[2, 3, 2], [3, 2, 10]]
    V = 10
    
    小S有 10 元预算,第一种货船最多可以租 2 艘,每艘承载 2 吨,总计 4 吨;第二种货船最多可以租 3 艘,每艘承载 10 吨,总计 30 吨。最终,最大总载货量为 32 吨。

总结

这道题是一个典型的“有数量限制的背包问题”,通过二进制拆解法优化了处理每种货船数量限制的效率。通过动态规划,最终可以在给定的预算限制内找到承载货物的最大总量。这类问题不仅考察了对背包问题的理解,还要求通过合适的算法优化来应对实际问题中的复杂性。