动态规划经典问题解析:混合背包、分组背包与二维费用背包
动态规划是算法设计中非常重要的思想方法,背包问题则是动态规划中最经典的案例之一。本文将深入解析三种常见的背包问题变种:混合背包问题、分组背包问题和二维费用背包问题,帮助读者掌握这些问题的解决思路和实现方法。
混合背包问题解析
混合背包问题是背包问题中最具综合性的变种之一,它融合了0-1背包、完全背包和多重背包三种情况。
问题定义
给定n种物品和一个容量为W的背包,每种物品具有以下特性:
- 重量weight[i]
- 价值value[i]
- 数量count[i]
其中count[i]的不同取值代表不同含义:
- count[i] = -1:物品只有1件(0-1背包情况)
- count[i] = 0:物品有无限件(完全背包情况)
- count[i] > 0:物品有count[i]件(多重背包情况)
解决思路
混合背包问题的核心在于识别每种物品的类型,并采用相应的处理策略:
- 二进制优化:首先将所有多重背包物品通过二进制拆分转换为0-1背包物品
- 分类处理:
- 对于0-1背包物品,直接保留
- 对于完全背包物品,标记并保留
- 动态规划求解:
- 使用一维dp数组,dp[w]表示容量为w时的最大价值
- 逆序遍历处理0-1背包物品
- 正序遍历处理完全背包物品
代码实现
class Solution:
def mixedPackMethod1(self, weight: [int], value: [int], count: [int], W: int):
weight_new, value_new, count_new = [], [], []
# 二进制优化处理多重背包
for i in range(len(weight)):
cnt = count[i]
if cnt > 0: # 多重背包
k = 1
while k <= cnt:
cnt -= k
weight_new.append(weight[i] * k)
value_new.append(value[i] * k)
count_new.append(1)
k *= 2
if cnt > 0:
weight_new.append(weight[i] * cnt)
value_new.append(value_new[i] * cnt)
count_new.append(1)
elif cnt == -1: # 0-1背包
weight_new.append(weight[i])
value_new.append(value[i])
count_new.append(1)
else: # 完全背包
weight_new.append(weight[i])
value_new.append(value[i])
count_new.append(0)
dp = [0] * (W + 1)
for i in range(len(weight_new)):
if count_new[i] == 1: # 0-1背包
for w in range(W, weight_new[i] - 1, -1):
dp[w] = max(dp[w], dp[w - weight_new[i]] + value_new[i])
else: # 完全背包
for w in range(weight_new[i], W + 1):
dp[w] = max(dp[w], dp[w - weight_new[i]] + value_new[i])
return dp[W]
复杂度分析
- 时间复杂度:O(W × Σlog₂count[i]),其中W为背包容量
- 空间复杂度:O(W)
分组背包问题详解
分组背包问题中,物品被分为若干组,每组中的物品相互冲突,最多只能选择一件。
问题定义
给定n组物品和一个容量为W的背包:
- 第i组有group_count[i]件物品
- 第i组第j件物品重量为weight[i][j],价值为value[i][j]
- 每组只能选择最多一件物品
基本解法
二维动态规划
定义dp[i][w]表示前i组物品放入容量为w的背包的最大价值。
状态转移方程:
dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i-1][k]] + value[i-1][k])
其中0 ≤ k < group_count[i-1]
优化解法(滚动数组)
使用一维数组优化空间复杂度:
def groupPackMethod2(self, group_count, weight, value, W):
dp = [0] * (W + 1)
for i in range(len(group_count)):
for w in range(W, -1, -1):
for k in range(group_count[i]):
if w >= weight[i][k]:
dp[w] = max(dp[w], dp[w - weight[i][k]] + value[i][k])
return dp[W]
复杂度分析
- 时间复杂度:O(W × Σgroup_count[i])
- 空间复杂度:O(W)
二维费用背包问题探究
二维费用背包问题在传统背包问题基础上增加了第二维限制条件(如体积)。
问题定义
给定n件物品和一个背包:
- 每件物品有重量weight[i]和体积volume[i]
- 背包有重量限制W和体积限制V
- 每件物品只能选一次
基本解法
三维动态规划
定义dp[i][w][v]表示前i件物品放入重量限制w、体积限制v的背包的最大价值。
状态转移方程:
dp[i][w][v] = max(dp[i-1][w][v],
dp[i-1][w-weight[i-1]][v-volume[i-1]] + value[i-1])
优化解法(二维数组)
def twoDCostPackMethod2(self, weight, volume, value, W, V):
dp = [[0]*(V+1) for _ in range(W+1)]
for i in range(len(weight)):
for w in range(W, weight[i]-1, -1):
for v in range(V, volume[i]-1, -1):
dp[w][v] = max(dp[w][v],
dp[w-weight[i]][v-volume[i]] + value[i])
return dp[W][V]
复杂度分析
- 时间复杂度:O(n×W×V)
- 空间复杂度:O(W×V)
总结与比较
这三种背包问题变种各有特点:
- 混合背包:综合多种背包类型,需要分类处理
- 分组背包:组内物品互斥,增加选择维度
- 二维费用:增加限制条件,扩展状态维度
理解这些问题的共性和差异,有助于我们掌握动态规划解决背包类问题的核心思想:定义合适的状态,找到正确的状态转移方程,并根据问题特点进行优化。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考