目录
Note: 本文主要从动态规划的一维、二维情况讲解了背包问题,主要记录了自己的理解过程
1、背包问题1——背包无价值
Lintcode 92题 https://www.lintcode.com/problem/backpack/description
在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]
样例 :
输入: [3,4,8,5], backpack size=10
输出: 9解析:
动态规划二维dp递推公式:
其中i => 0 ~ n, j => 0 ~ m+1
:前i个物品放入容量为j的背包的最大重量;
:放入第i个物品时的重量;
其中i-1为上一个物品, j-A[i] 表示当前容量j - 当前物品重量A[i] 时背包所能容纳物品的重量;
dp[i-1][j]:不放第i个时的重量;
动态规划一维dp
递推公式:
j => m ~ A[i] 倒序,每个物品不重复计算
二维动态规划
def backpack(m, A):
n = len(A)
if n <= 0 or m <= 0:
return 0
# 行 m+1, 列 len(A)
dp = [[0]*(m + 1) for _ in range(n)]
# 初始化dp首行, A[0]为第一个物品的重量, i从0~m+1表示当前背包容量i
# A[0] < i 表示当前背包容量能放入A[0]
for i in range(m+1):
if A[0] <= i:
dp[0][i] = A[0]
for i in range(1, n):
for j in range(1, m+1):
if A[i] > j: # 当前物品的重量大于背包的容量, 则取i-1之前的最大重量
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j-A[i]] + A[i], dp[i-1][j])
return dp[-1][-1]
if __name__ == '__main__':
m = 10
A = [3, 4, 8, 5]
print('二维dp ', backpack(m, A))
二维dp 动态更新过程:
一维动态规划
def backpack(m, A):
n = len(A)
if n <= 0 or m <= 0:
return 0
dp = [0] * (m+1)
# i: 0 ~ n
for i in range(n):
# j: m ~ A[i] - 1, 倒序, 每个物品用一次
for j in range(m, A[i]-1, -1):
dp[j] = max(dp[j-A[i]] + A[i], dp[j])
return dp[-1]
if __name__ == '__main__':
m = 10
A = [3, 4, 8, 5]
# 一维dp数组变化过程跟二维一样, 区别一维在原地更新
print('一维dp ', backpack(m, A))
2、背包问题Ⅱ——背包有价值
Lintcode 125. 背包问题 II https://www.lintcode.com/problem/backpack-ii/description
有
n
个物品和一个大小为m
的背包. 给定数组A
表示每个物品的大小和数组V
表示每个物品的价值.问最多能装入背包的总价值是多大?
样例 :
输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
输出: 9
解释: 装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9思路同上:
数组中存放的是当前的最大价值
def backpack1(m, A, V):
n = len(A)
if n <= 0 or m <= 0:
return 0
dp = [0] * (m+1)
for i in range(n):
for j in range(m, A[i]-1, -1):
dp[j] = max(dp[j-A[i]] + V[i], dp[j])
return dp[-1]
def backpack2(m, A, V):
n = len(A)
if n <= 0 or m <= 0:
return 0
# 行 m+1, 列 len(A)
dp = [[0]*(m + 1) for _ in range(n)]
# 初始化 首行
for i in range(m+1):
if A[0] <= i:
dp[0][i] = V[0]
for i in range(1, n):
for j in range(1, m+1):
if A[i] > j: # 当前价值大于总价值, 则取i-1之前的最大价值
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j-A[i]] + V[i], dp[i-1][j])
return dp[-1][-1]
if __name__ == '__main__':
m = 10
A = [2, 3, 5, 7]
V = [1, 5, 2, 4]
print('一维dp ', backpack1(m, A, V))
print('二维dp ', backpack2(m, A, V))
3、背包问题Ⅲ——完全背包问题
LintCode 440: Backpack III https://www.lintcode.com/problem/backpack-iii/ 需要会员
给定n种大小为Ai和值Vi的物品(每个物品都有无限个可用数量)和一个大小为m的背包。你能放进背包里的最大值是多少?
样例 :
输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
输出: 9
解释: 装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9解析:
每种物品取的上限是 m / A[i]
动态规划二维dp
递推公式:
其中 i => 0 ~ n,j => 0 ~ m+1,k => 0 ~ A[i] *k < j
:前i个物品放入容量为j的背包的最大价值;
:放入第i个物品,数量为k时的当前价值;
其中
为上一个物品,
表示当前容量j - 当前k个物品重量A[i] 时背包所能容纳物品的重量;
:等号右边
,放入第i个物品,数量为k时的最大价值;
:不放第i个时的重量;
动态规划一维dp
递推公式:
j => A[i] ~ m+1 每个物品可重复计算
二维动态规划
def backpack(m, A, V):
n = len(A)
if n <= 0 or m <= 0:
return 0
# 行 m+1, 列 len(A)
dp = [[0]*(m + 1) for _ in range(n)]
# 初始化首行, i(当前背包容量0 ~ m+1) / A[0](重量) * V[0](价值)
for i in range(m+1):
dp[0][i] = i // A[0] * V[0]
for i in range(1, n):
for j in range(1, m+1):
for k in range(j):
if k*A[i] <= j:
dp[i][j] = max(dp[i-1][j-k*A[i]]+k*V[i], dp[i-1][j], dp[i][j])
else:
break
return dp[-1][-1]
if __name__ == '__main__':
m = 10
A = [2, 3, 5, 7]
V = [1, 5, 2, 4]
# 15
print(backpack1(m, A, V))
一维动态规划
def backpack(m, A, V):
n = len(A)
if n <= 0 or m <= 0:
return 0
dp = [0] * (m+1)
# i: 0 ~ n
for i in range(n):
# j: A[i] ~ m+1 重复使用
for j in range(A[i], m+1):
dp[j] = max(dp[j-A[i]] + V[i], dp[j])
return dp[-1]
if __name__ == '__main__':
m = 10
A = [2, 3, 5, 7]
V = [1, 5, 2, 4]
# 15
print(backpack(m, A, V))