0-1背包
- p [ i ] [ j ] p[i][j] p[i][j]中, i i i表示背包中放入第 i i i个物体, j j j表示当前背包的容量大小
- p [ i ] [ j ] = m a x ( p [ i − 1 ] [ j ] , p [ i − 1 ] [ j − w e i g h t [ i − 1 ] ] + v a l u e [ i − 1 ] ) p[i][j]=max(p[i-1][j],p[i-1][j-weight[i-1]]+value[i-1]) p[i][j]=max(p[i−1][j],p[i−1][j−weight[i−1]]+value[i−1])表示,放入第 i i i个物体时,我们需要判断是否需要放入这个物体,会使价值更高。
- p [ i − 1 ] [ j ] p[i-1][j] p[i−1][j]表示不放入第 i i i个物体,价值是多少
- p [ i − 1 ] [ j − w e i g h t [ i − 1 ] ] + v a l u e [ i − 1 ] p[i-1][j-weight[i-1]]+value[i-1] p[i−1][j−weight[i−1]]+value[i−1]表示放入第 i i i个物体时,用现在容量减去 i i i物体的重量后背包的总价值加上i物体的价值
def knapback(weight, value, n):
len_weight = len(weight)
p = [[0]*(n+1) for i in range(len_weight + 1)]
for i in range(1, len_weight+1):
for j in range(1, n+1):
if weight[i-1] > j:
p[i][j] = p[i-1][j]
else:
p[i][j] = max(p[i-1][j], p[i-1][j-weight[i-1]] + value[i-1])
return p[len_weight][n], p
0-1背包的优化,只能在空间上优化
我们可以看出 p [ i ] [ j ] p[i][j] p[i][j]和 p [ i − 1 ] [ j ] p[i-1][j] p[i−1][j]有关系,正向推,就是上述逻辑,由 p [ i − 1 ] [ j ] p[i-1][j] p[i−1][j]推出 p [ i ] [ j ] p[i][j] p[i][j],反向推,就可以由 [ i ] [ j ] [i][j] [i][j]推出 p [ i − 1 ] [ j ] p[i-1][j] p[i−1][j]
def rev_knapback(weight, value, n):
len_weight = len(weight)
p = [0] * (n + 1)
for i in range(0, len_weight):
for j in range(n, -1, -1):
if j >= weight[i]:
p[j] = max(p[j], p[j - weight[i]] + value[i])
return p
完全背包
- p [ i ] [ j ] = m a x ( p [ i − 1 ] [ j ] , p [ i ] [ j − w e i g h t [ i − 1 ] ] + v a l u e [ i − 1 ] ) p[i][j]=max(p[i-1][j],p[i][j-weight[i-1]]+value[i-1]) p[i][j]=max(p[i−1][j],p[i][j−weight[i−1]]+value[i−1]),有改动的地方就在于每个物体都可以重复装入背包,所以,要在当前的容量下减去当前物体的重量
- p [ i ] [ j − w e i g h t [ i − 1 ] ] + v a l u e [ i − 1 ] p[i][j-weight[i-1]]+value[i-1] p[i][j−weight[i−1]]+value[i−1]表示放入第 i i i个物体时,用现在容量减去 i i i物体的重量后背包的总价值加上 i i i物体的价值
def knapback(weight, value, n):
len_weight = len(weight)
p = [[0]*(n+1) for i in range(len_weight + 1)]
for i in range(1, len_weight+1):
for j in range(1, n+1):
if weight[i-1] > j:
p[i][j] = p[i-1][j]
else:
p[i][j] = max(p[i-1][j], p[i][j-weight[i-1]] + value[i-1])
return p[len_weight][n], p
0-1背包的变种问题
- 求数组和为s的所有组合方式,arr = [5, 5, 10, 2, 3],s = 15
- 相对于背包问题不同的是这里的 p [ i ] [ j ] = p [ i − 1 ] [ j ] + p [ i − 1 ] [ j − a r r [ i − 1 ] ] p[i][j]=p[i-1][j]+p[i-1][j-arr[i-1]] p[i][j]=p[i−1][j]+p[i−1][j−arr[i−1]]
- p [ i − 1 ] [ j ] p[i-1][j] p[i−1][j]:在不加上当前的这个数字时,和为 j j j的方法总数
-
[
i
−
1
]
[
j
−
a
r
r
[
i
−
1
]
]
[i-1][j-arr[i-1]]
[i−1][j−arr[i−1]]:表示在加上当前这个数字时,和为
j
j
j的方法总数
def method_nums(arr, target):
len_arr = len(arr)
p = [[0 for i in range(target + 1)] for j in range(len_arr + 1)]
for i in range(len_arr + 1):
p[i][0] = 1
for i in range(1, len_arr + 1):
for j in range(1, target + 1):
if arr[i-1] <= j:
p[i][j] = p[i-1][j] + p[i-1][j-arr[i-1]]
else:
p[i][j] = p[i-1][j]
return p