一.问题描述
小S在码头租赁货船,有Q种不同类型的货船可供选择。每种货船有固定的数量m[i],租赁成本v[i]和最大载货量m[i]。小S希望在预算V元内,租用能够承载最大总货物的货船组合。每种货船的基本信息包括数量,租赁价格和载货量。小S需要你帮忙计算在给定预算下,她能租用的货船的最大载货量是多少。 Q:货船的种类数量 V:李华可用的总预算(单位:元) ships:一个列表
二.解决问题思路
解决这个问题的思路主要是使用动态规划。
1. 状态定义:
定义 dp[i][j]
表示在前 i
种货船中,预算为 j
时能承载的最大货物量。
2. 状态转移方程:
对于第 i
种货船,如果其租赁成本 cost
大于当前预算 j
,那么不能选择这种货船,此时 dp[i][j] = dp[i - 1][j]
,即与不考虑第 i
种货船时的最大载货量相同。
如果 cost
小于等于 j
,则有两种选择:
不选择第 i
种货船,此时最大载货量为 dp[i - 1][j]
。
选择第 i
种货船,此时最大载货量为 dp[i - 1][j - cost] + cargo
(cargo
为第 i
种货船的载货量)。
取这两种情况的最大值,即 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cost] + cargo)
。
3. 最终结果:
dp[Q][V] 表示在所有
Q 种货船中,预算为
V` 时能承载的最大货物量,这就是最终的答案。
通过动态规划的方法,逐步计算并保存中间结果,避免了重复计算,高效地解决了在给定预算下选择货船以获得最大载货量的问题。
三.代码详解
def solution(Q, V, ships):
# 使用动态规划来解决此问题
# 创建一个二维数组 dp,dp[i][j] 表示在前 i 种货船中,预算为 j 时能承载的最大货物量。
dp = [[0 for _ in range(V + 1)] for _ in range(Q + 1)] # 1. 定义并初始化 dp 数组
for i in range(1, Q + 1): # 2. 开始遍历每种货船
m, v, w = ships[i - 1]
for j in range(1, V + 1): # 对于每种货船,遍历预算
dp[i][j] = dp[i - 1][j] # 不租用第 i 种货船的情况
k = min(m, j // v) # 计算能租用的数量
for x in range(k + 1): # 考虑租用不同数量的第 i 种货船
if j >= x * v:
dp[i][j] = max(dp[i][j], dp[i - 1][j - x * v] + x * w) # 更新最大载货量
return dp[Q][V] # 返回最终结果
if __name__ == "__main__":
ships = [[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, ships) == 32)
print(solution(23, 400, ships2) == 1740)
四.代码具体解析
1.函数 solution 接受三个参数:
Q:表示不同类型货船的数量。 V:表示小 S 的预算。 ships:是一个列表,其中每个元素是一个三元组 (num_ships, cost, cargo),分别表示某种货船的数量、租赁成本和载货量。
2.在函数内部:创建了一个二维列表 dp
大小为 (Q + 1) x (V + 1),这个二维列表用于存储动态规划过程中的中间结果。 外层循环 for i in range(1, Q + 1): 遍历每一种货船。 内层循环 for j in range(1, V + 1): 遍历每个预算值。
3.对于每一种货船和每个预算值,进行以下判断和计算:
如果当前货船的租赁成本 cost 大于当前预算 j,那么不能选择这种货船,此时 dp[i][j] 的值就等于不考虑当前货船时的最大载货量,即 dp[i - 1][j]。 如果 cost 小于等于 j,则有两种选择: 不选择当前货船,最大载货量为 dp[i - 1][j]。 选择当前货船,此时最大载货量为 dp[i - 1][j - cost] + cargo。这里 dp[i - 1][j - cost] 表示在选择当前货船之前,预算为 j - cost 时的最大载货量,再加上当前货船的载货量 cargo。取这两种情况的最大值,即 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cost] + cargo)。
4.最后
返回 dp[Q][V],这表示在所有 Q 种货船中,预算为 V 时能承载的最大货物量。 在主函数中,设置了测试数据并调用 solution 函数进行测试,最后打印出结果。
五.个人反思与总结
1.在初始化 dp 数组时,将 dp[0][j] 和 dp[i][0] 都设为 0。这是合理的,因为当没有货船可选(i = 0)或者预算为 0 时,载货量必然为 0。这种边界条件的处理为后续的动态规划计算提供了基础。在其他动态规划问题中,边界条件和初始化需要根据问题的实际情况仔细考虑。错误的初始化可能会导致整个计算结果的错误,所以要确保初始化的值符合问题的逻辑。
2.在实现状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cost] + cargo) 时,需要清晰地理解每个部分的含义。特别是对于条件判断(if cost > j),要准确地处理不同情况下的状态转移。
3.在编写代码实现状态转移方程时,容易出现逻辑错误,比如索引错误或者计算顺序错误。需要仔细检查和测试,确保状态转移的计算是正确的。同时,可以通过添加一些调试输出或者使用简单的测试用例来验证状态转移方程的实现是否符合预期。