题解:巧克力背包问题
问题描述
在一次春游中,小M希望尽可能多地携带巧克力板。她拥有 nnn 块巧克力板,每块巧克力的边长为 aia_iai,而重量则为 ai2a_i^2ai2。小M有多个不同大小的背包,每个背包都有一个最大承重限制。我们的目标是帮助小M计算在每个背包的最大承重范围内,她最多可以携带多少块巧克力板。
例如,假设小M有5块巧克力板,边长分别为1, 2, 2, 4, 5。对于5个不同的背包,其最大承重分别为1, 3, 7, 9, 15,经过计算,小M能够装入的巧克力块数分别是1, 1, 2, 3, 3。
问题分析
该问题可以归类为一个背包问题,具体来说是0/1背包问题的变种。在0/1背包问题中,每个物品只能被选择一次,且我们需要在给定的重量限制下选择尽可能多的物品。我们可以使用动态规划来解决这个问题。
- 巧克力的重量计算: 每块巧克力的重量是边长的平方,即对于边长 aia_iai,其重量为 ai2a_i^2ai2。
- 背包的承重限制: 对于每个背包,我们需要考虑其最大承重,找出可以装入的巧克力块数。
动态规划思路
我们将使用动态规划来解决这个问题,基本思路如下:
- 定义一个数组
dp
,其中dp[j]
表示承重为 jjj 时能够装入的最大巧克力块数。 - 我们遍历每块巧克力板,更新
dp
数组,从后向前更新,以避免在同一轮中重复考虑同一块巧克力。
实现步骤
-
初始化:
- 计算每块巧克力的重量。
- 找到所有背包的最大承重,设为
max_weight
。 - 创建一个
dp
数组,大小为max_weight + 1
,初始值为0。
-
动态规划填充:
- 对于每块巧克力的重量,从最大承重开始向前更新
dp
数组。 - 如果当前背包的承重可以装下该巧克力,则更新
dp[j]
为max(dp[j], dp[j - weight] + 1)
。
- 对于每块巧克力的重量,从最大承重开始向前更新
-
结果输出:
- 遍历每个背包的最大承重,输出对应的
dp
值。
- 遍历每个背包的最大承重,输出对应的
代码实现
以下是完整的Python代码实现:
def solution(n , m , chocolates, bags):
# 计算每块巧克力的重量
weights = [a ** 2 for a in chocolates]
# 找到最大的背包承重限制
max_weight = max(bags)
# 初始化动态规划数组,大小为 max_weight + 1
dp = [0] * (max_weight + 1)
# 动态规划求解
for weight in weights:
# 从后往前更新 dp 数组
for j in range(max_weight, weight - 1, -1):
dp[j] = max(dp[j], dp[j - weight] + 1)
# 针对每个背包的最大承重输出结果
results = []
for bag in bags:
results.append(dp[bag])
return results
# 测试用例:
chocolates = [1, 2, 2, 4, 5]
bags = [1, 3, 7, 9, 15]
print(max_chocolates(chocolates, bags)) # 输出:[1, 1, 2, 3, 3]
代码解析
- 权重计算:通过列表推导式
weights = [a ** 2 for a in chocolates]
计算每块巧克力的重量。 - 动态规划数组:
dp
数组初始化为0,大小为max_weight + 1
,表示每个承重状态。 - 填充动态规划数组:内层循环从
max_weight
到weight
更新dp
,确保每块巧克力只被计算一次。 - 结果生成:最后遍历每个背包的最大承重,从
dp
数组中提取结果。
复杂度分析
- 时间复杂度:最坏情况下时间复杂度为 O(n⋅m)O(n \cdot m)O(n⋅m),其中 nnn 是巧克力块的数量,mmm 是最大的背包承重。
- 空间复杂度:使用了 O(m)O(m)O(m) 的空间来存储动态规划数组。