题目解析
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. 动态规划建模
这是一个多重背包问题,解决方法为动态规划:
-
状态定义:
- 使用 dp[j]dp[j] 表示在预算 jj 下,可以达到的最大总载货量。
-
状态转移:
- 对于第 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),表示不能超过货船数量限制,且租赁总成本不超预算。
-
边界条件:
- 初始时,dp[0]=0dp[0] = 0(预算为 0 时,总载货量为 0)。
-
优化方向:
- 从大到小遍历预算 jj,确保当前决策不会干扰后续决策。
2. 解题步骤
-
初始化一个大小为 V+1V+1 的数组 dpdp,用来记录不同预算下的最大载货量。
-
遍历每种货船,对每个预算 jj:
- 枚举租用该货船的数量 kk,更新 dp[j]dp[j]。
-
最终,返回 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]]
-
初始 dp=[0,0,0,…,0]dp = [0, 0, 0, \dots, 0](长度为 V+1=11V+1 = 11)。
-
遍历第 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)。
-
-
遍历第 2 种货船(3 艘,价格 2,载货量 10):
- 类似更新 dpdp,最终得到 dp[10]=32dp[10] = 32。
输出:32
复杂度分析
-
时间复杂度:
- 外层循环 QQ 次(货船种类)。
- 每次遍历预算 VV,枚举货船数量 kk,复杂度为 O(Q⋅V⋅K)O(Q \cdot V \cdot K),其中 KK 是单种货船的最大租用数量。
-
空间复杂度:
- O(V)O(V) 用于存储 dpdp 数组。
优化可以采用二进制分组优化枚举 kk,减少复杂度到 O(Q⋅V⋅logM)O(Q \cdot V \cdot \log M),其中 MM 是单种货船最大数量。