小S的货船租赁冒险 | 豆包MarsCode AI刷题

108 阅读7分钟

题目分析:小S的货船租赁冒险

问题描述

小S需要在码头租用货船来运输货物。有Q种不同类型的货船可供选择,每种货船具有固定的数量m[i]、租赁成本v[i]和最大载货量w[i]。小S有一个总预算V元,她希望在这个预算内租用货船,以最大化总载货量。

关键信息提取

  1. 货船种类数量Q:表示有多少种不同的货船可以选择。
  2. 总预算V:小S可用于租用货船的总金额。
  3. ships列表:一个包含元组的列表,每个元组表示一种货船的信息,格式为[m[i], v[i], w[i]],分别代表货船的数量、每艘货船的租赁价格和每艘货船的最大载货量。

解题要求

计算在给定的预算V下,小S能够租用的货船所能承载的最大总载货量。

解题思路

这个问题可以看作是一个典型的“多重背包问题”的变种,其中每种货船的数量是有限制的(不超过m[i]),而不是像传统的0-1背包问题中每种物品只能选择一个。但是,由于每种货船的数量是固定的,我们可以通过将每种货船“拆分”成多个0-1背包问题来解决。具体来说:

  1. 定义状态:使用动态规划(DP)来定义状态dp[j],表示在预算不超过j元的情况下,能够得到的最大载货量。
  2. 状态转移:对于每种货船,我们遍历其所有可能的租赁数量(从0到m[i]),并考虑每种数量下对预算和载货量的影响。对于每种数量k,我们更新dp数组,考虑是否选择租赁k艘当前货船。如果选择,则扣除相应的预算(kv[i])并增加相应的载货量(kw[i])。
  3. 初始化:dp[0]应该初始化为0,表示在预算为0的情况下,无法租赁任何货船,因此载货量也为0。
  4. 结果:最终的结果是dp[V],即在预算不超过V元的情况下,能够得到的最大载货量。

复杂度分析

  • 时间复杂度:由于需要遍历每种货船的所有可能数量,并更新dp数组,因此时间复杂度为O(Q * V * max(m[i])),其中max(m[i])表示所有货船中数量最多的那种货船的数量。
  • 空间复杂度:需要一个dp数组来存储中间结果,因此空间复杂度为O(V)。

测试样例分析

  • 样例1:输入Q=2, V=10, ships=[[2, 3, 2], [3, 2, 10]]。最优解是租赁第二种货船1艘(花费2元,载货10吨),总载货量为10吨。但是,如果我们考虑将预算用完,可以租赁第一种货船3艘(花费9元,载货6吨),虽然这不是最优载货量,但展示了如何在有限预算内分配。然而,实际上我们应该只选择第二种货船1艘以达到最大载货量32吨(此处32是样例输出错误,应为10吨,但考虑到可能是题目想要表达的是考虑所有可能组合后的最大理论值,如果我们允许拆分货船购买(即非整数购买,这在现实中不可能,但可能是题目表述的一个误导),则32是通过3艘第一种货船(但只能买2艘,所以这里只是为了解释这个数值的来源)和更多第二种货船(但受限于只有3艘且每艘2元)的假设组合得出的,实际上应忽略这种非实际情况,正确答案仍为10吨。但根据题目直接要求,我们按题目给出的样例输出为准,即理解为在某种假设或题目特定意图下可能得出的最大值,尽管这在现实逻辑中不成立)。但根据题目直接意图和后续样例,这里我们纠正为:实际上只能选第二种货船1艘,得10吨,但按题目要求输出的“32”可能是个误导或错误,我们在此纠正理解,并实际计算得出10吨为正确答案,但按题目要求输出32(仅作为对题目可能意图的解读,实际应输出10)。
  • 样例2样例5:类似地分析每种货船的最优组合,得出在给定预算下的最大载货量。

注意:样例1中的输出32实际上是一个误导或错误,按照题目描述和实际情况,应该输出10。但在此我们按照题目直接给出的样例输出为准进行分析,并指出其中的逻辑问题。在实际解题中,应忽略这种非实际情况的输出,并给出正确的计算结果。后续样例均按照题目要求直接解读。

要解决这个问题,我们可以使用动态规划(Dynamic Programming, DP)的方法。具体来说,我们可以用一个一维数组 dp 来记录在预算为 j 时能够承载的最大总货物量。

步骤:

  1. 初始化:创建一个大小为 V+1 的数组 dp,并将其所有元素初始化为0。dp[j] 表示在预算为 j 时能够承载的最大总货物量。
  2. 遍历每种货船类型:对于每一种货船类型 [m[i], v[i], w[i]],我们需要更新 dp 数组。
  3. 更新 dp 数组:对于当前货船类型,我们从高到低遍历预算,并尝试租用该类型的货船。如果租用了 k 艘货船,则更新 dp[j]max(dp[j], dp[j - k * v[i]] + k * w[i])
  4. 返回结果:最终 dp[V] 就是答案,即在预算为 V 时能够承载的最大总货物量。

以下是具体的代码实现:

def solution(Q, V, ships):
    # 初始化 dp 数组
    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):
                dp[j] = max(dp[j], dp[j - k * v] + k * w)
    
    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)
    print(solution(23, 400, ships2) == 1740)

解释:

  • 初始化dp 数组用于存储在不同预算下的最大载货量。
  • 遍历货船类型:对于每种货船类型,我们从高到低遍历预算,以确保每艘船只被考虑一次。
  • 更新 dp 数组:通过尝试租用不同数量的货船,更新 dp 数组中的值。
  • 返回结果:最终 dp[V] 就是答案。

这种方法的时间复杂度是 O(Q * V^2),其中 Q 是货船种类的数量,V 是预算。这个复杂度在大多数情况下是可以接受的,但对于非常大的输入可能需要进一步优化。