这道题目本质上是一个“01背包问题”的变种。我们需要在给定预算内选择一系列货船,以最大化可承载的货物总重量。题目中的货船不仅有数量限制,而且每种货船的租赁价格和载货量也是不同的,如何在预算限制下选择最优组合是一道典型的背包问题。
问题分析
我们可以从以下几个方面进行分析:
-
输入:
Q:表示货船种类的数量。V:表示总预算,限制了我们可以选择的货船组合的总费用。ships:一个列表,其中每个元素是一个元组,包含三部分:m[i]:第i种货船的数量。v[i]:第i种货船的租赁价格。w[i]:第i种货船的载货量。
-
输出:
- 需要输出在预算
V内能够租用的货船组合能够承载的最大总货物。
- 需要输出在预算
背包问题的应用
这是一个典型的“0-1背包问题”,但具有一定的复杂性,因为每种货船不仅有价格和载货量,还有限定的数量。常规的背包问题每个物品只有一件,而这里每个货船种类可能有多个,且数量有限。为了应对这一点,通常有两种方法来解决:
- 完全背包:允许多次选择某个物品。
- 01背包:每种物品只能选择一次。
这里的货船种类是“有数量限制的物品”,可以通过“二进制拆解法”来处理。具体来说,我们可以通过将每个货船的数量分解成几个不同的“虚拟物品”来简化问题。例如,如果一种货船有10艘,我们可以将其拆成1艘、2艘、4艘、8艘这样的组合,使得每次选择的货船数量为2的幂次方。
解法思路
我们使用动态规划(Dynamic Programming, DP)来解决这个问题。假设 dp[j] 表示在预算为 j 元时,能够承载的最大货物重量。我们的目标是求出 dp[V],即在预算 V 内的最大承载重量。
- 初始化:
dp[0] = 0,表示当预算为0时,无法承载任何货物。 - 状态转移:对于每一种货船,我们需要更新
dp数组。由于是有限数量的物品,我们采用“二进制拆解法”,将每种货船的数量拆解成若干个“虚拟物品”来更新dp数组。 - 二进制拆解:如果第
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]
代码解析
-
初始化DP数组:
dp数组用来记录在给定预算下能承载的最大载货量。dp[j]表示预算为j时的最大载货量,初始时,所有的dp[j]都为0,表示没有租赁货船时载货量为0。 -
二进制拆解法:对于每种货船,首先我们通过二进制拆解法将其拆解成多个子问题。对于每个拆解出的数量(比如 1 艘、2 艘、4 艘货船),逐步更新
dp数组。这样能够确保每种货船的数量不会超过其限制。 -
状态转移:每次选择了某种数量的货船后,更新
dp数组,尝试在预算限制内获取最大载货量。 -
结果输出:最终,
dp[V]就是预算V下能够承载的最大货物总重量。
复杂度分析
- 时间复杂度:每种货船通过二进制拆解法最多会进行
log(m[i])次更新,因此总的时间复杂度为O(Q * log(m[i]) * V),其中Q是货船种类的数量,m[i]是第i种货船的数量,V是预算。 - 空间复杂度:我们只使用了一个长度为
V+1的dp数组,所以空间复杂度是O(V)。
测试案例解析
- 测试样例 1:
小S有 10 元预算,第一种货船最多可以租 2 艘,每艘承载 2 吨,总计 4 吨;第二种货船最多可以租 3 艘,每艘承载 10 吨,总计 30 吨。最终,最大总载货量为 32 吨。ships = [[2, 3, 2], [3, 2, 10]] V = 10
总结
这道题是一个典型的“有数量限制的背包问题”,通过二进制拆解法优化了处理每种货船数量限制的效率。通过动态规划,最终可以在给定的预算限制内找到承载货物的最大总量。这类问题不仅考察了对背包问题的理解,还要求通过合适的算法优化来应对实际问题中的复杂性。