0-1背包问题
问题描述:
有 n 件物品和一个最大承重为 W 的背包,每件物品的重量是 𝑤i、价值是 𝑣i 在保证总重量不超过 W的前提下,选择某些物品装入背包,背包的最大总价值是多少?
注意:每个物品只有 1 件,也就是每个物品只能选择 0 件或者 1 件
定义状态
#values:是价值数组;weights:是重量数组
假设dp(i, j)是最大承重为j,有前i件物品可选时的最大总价值,
初始值设定
dp(i, 0)、dp(0, j)初始值均为0
动态转移方程
如果j < weights[i - 1],则无法选择最后一件物品,则dp[i][j] = dp[i - 1][j]
如果j >= weightsi - 1,则可选可不选,则dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]])
def maxValues(values, capacity, weights):
if values == None or len(values) == 0:
return 0
if weights == None or len(weights) == 0:
return 0
if len(values) != len(weights) or capacity <= 0:
return 0
#设定一个长为capacity,宽为len(values)的二维数组
dp = [[0] * (capacity + 1) for _ in range(len(values) + 1)]
for i in range(1, len(values) + 1, 1):
for j in range(1, capacity + 1, 1):
if j < weights[i - 1]:
continue
else:
dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]])
return dp[len(values)][capacity]
以j = 10这列为例:
第一个物品v= 6, w = 2可选可不选,如果不选则dp[1][10] = dp[0][10],如果选,则dp[1][10] = 6 + dp[0][10 - 2] = 6,所以最终dp[1][10] = 6
依次类推…
优化:将二维dp数组优化为一维动态数组
由于在计算dp[i][j]时可能会用到dp[i - 1][j - weights[i - 1]]的值,因此最好是从右往左进行计算,这样新计算到的值不会影响前面可能用到的值
def maxValues(values, capacity, weights):
if values == None or len(values) == 0:
return 0
if weights == None or len(weights) == 0:
return 0
if len(values) != len(weights) or capacity <= 0:
return 0
#设定一个长为capacity
dp = [0] * (capacity + 1)
for i in range(1, len(values) + 1, 1):
for j in range(capacity, 0, -1):
if j < weights[i - 1]:
continue
else:
dp[j] = max(dp[j], values[i - 1] + dp[j - weights[i - 1]])
return dp[capacity]
继续优化
j在遍历时,从capacity到weights[i - 1]即可,因为再继续遍历下去,j的值会比weights[i - 1]的值小,因此一直无法选择weights[i - 1]这个值
def maxValues(values, capacity, weights):
if values == None or len(values) == 0:
return 0
if weights == None or len(weights) == 0:
return 0
if len(values) != len(weights) or capacity <= 0:
return 0
#设定一个长为capacity
dp = [0] * (capacity + 1)
for i in range(1, len(values) + 1, 1):
for j in range(capacity, weights[i - 1] - 1, -1):
# if j < weights[i - 1]:
# continue
# else:
dp[j] = max(dp[j], values[i - 1] + dp[j - weights[i - 1]])
return dp[capacity]
问题变形
有 n 件物品和一个最大承重为 W 的背包,每件物品的重量是 𝑤i、价值是 𝑣i 在保证总重量恰好等于 W的前提下,选择某些物品装入背包,背包的最大总价值是多少? 注意:每个物品只有 1 件,也就是每个物品只能选择 0 件或者 1 件
values:是价值数组;weights:是重量数组 假设dp(i, j)是最大承重为j,有前i件物品可选时的最大总价值, dp(i,0)初始值为0,总重量恰好为0,最大总价值必然为0
dp(0, j) = -∞,j >= 1, 负数在这里代表无法恰好装满,只能用inf,保证无法恰好装满的值一直为inf 如果j < weights[i - 1],则无法选择最后一件物品,则dp[i][j] = dp[i - 1][j] 如果j >= weightsi - 1,则可选可不选,则dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]])
def maxVlaues_(values, capacity, weights):
if values == None or len(values) == 0:
return 0
if weights == None or len(weights) == 0:
return 0
if len(values) != len(weights) or capacity <= 0:
return 0
dp = [0] * (capacity + 1)
for j in range(1, capacity + 1, 1):
dp[j] = -float('inf')
for i in range(1, len(values) + 1, 1):
for j in range(capacity, weights[i - 1] - 1, -1):
dp[j] = max(dp[j], values[i - 1] + dp[j - weights[i - 1]])
return dp[capacity] if dp[capacity] > 0 else -1