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

28 阅读3分钟

叠甲

比较水,求轻喷

题目描述

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

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

思路解析

这道题是一道经典的多重背包问题。多重背包问题的基本描述是:有N个物品,每个物品i有重量w[i]和价值v[i],并且每种物品最多可以有m[i]件。背包的总容量为V。目标是选择物品装入背包,使得背包内的物品总价值最大,同时不超过背包的容量限制,并且每种物品的装入数量不能超过其上限。

一个比较简单的思路是将多重背包问题转化为0-1背包问题。即我们将选择多件第i件物品转化为,有m[i]件相同的物品,我们对每一件物品都可以选择选与不选。

在0-1背包问题中,我们最后将问题转化为对于前i件物品,我们求背包容积为j时可以达到的最大价值,可以得到的递推公式为f[i][j]=max(f[i-1][j], f[i-1][j-w[i]] + v[i])。考虑到f[i][j]的值只与前(i-1)个物品,即f[i-1]有关,因此我们可以将0-1背包问题转化为如果背包容积为j,递推公式为f[j]=max(f[j], f[j-w[i]]+v[i])。

代码

在这个多重背包问题中,我们ships[i][0]即为物品的数目,ships[i][1]即为物品的重量(这里时雇佣的费用,总容积用预算表示),ships[i][2]即为物品的价值(可载重)

根据0-1背包问题的思路,我们首先分配一个长为V+1的数组,表示总预算为下标时能载的最大重量。之后从V已知遍历到v[i],这是因为在0-1背包问题中,dp[i][j]会被dp[i][j-w[i]]影响。相当于物品i被多次放入背包。

同时我们需要考虑船的数目,我们可以从1已知遍历到m[i],表示选择船的数目的过程。当k * ships[i][1] > jk>m[i]时,停止遍历。

由此,可以得到算法的时间复杂度为

O(Wi=1km[i])O(W\sum_{i=1}^km[i])
​
def solution(Q, V, ships):
    # Please write your code here
    dp = [0] * (V + 1)
    for i in range(Q):
        for j in range(V, ships[i][1] - 1, -1):
            for k in range(1, ships[i][0] + 1):
                if k * ships[i][1] > j:
                    break
                dp[j] = max(dp[j], dp[j - k * ships[i][1]] + k * ships[i][2])
​
    return dp[V]

优化

接下来的是直接借鉴的内容,并没有自己实现

由于任何数字都可以转化为二进制表示法。也即

n=i=0ai2in=\sum_{i=0} a_i2^i

也就是说1, 2, 4, ..., 2^n可以表示一个(n+1)位的数字。我们可以将每个物品的数目按照二进制拆分,即表示为1个物品、2个物品、。。。、2^k个物品组成的物品序列。取得的物品数目即用二进制表示。

这样,将时间复杂度优化到了