经典01背包问题 + 状态回溯选择
用迭代方式解决01背包问题
除了返回背包能装入的最大价值外,可以再用一个二维数组记录每次选择的状态,用这一思路来跟踪每一步所做的选择,并从中回溯出一条最佳路线来。
不带选择模式的信息
只能获得背包能装入的最大价值
- i 轴表示 各个物品
- j轴表示包的大小
每次遍历的状态以及状态转移
dp[i][j]
: 以(i,j)为右下角点的矩形空间中所能获得的最大价值
状态转移:
当前背包容量与当前物品的重量比较 | dp[i][j]状态更新 |
---|---|
小于 | 继承(i-1, j)的状态 |
大于 | 此时分两种情况:取最大值 max(dp[i-1][j], value) |
- 情况1:对于
dp[i][j]
选择当前物品wt[i-1] + dp[i-1][ j - wt[j - 1] ]
- 情况2:对于
dp[i][j]
不选当前物品,继承dp[i-1][j]
的状态 - 在背包大小维度单调非减, 可以这么理解:随着背包的容量增加,在物品这一维度值不变的情况下,
dp[i][j]
在i不变的情况下,随着j的增加,dp[i][j]
的值是单调非减的。
# %%
# N : 物品的数量, W:背包的大小
N, W = 4, 4
wt = [4, 3, 1, 1]
val = [3000, 2000, 1500, 2000]
dp = [[0]*(W+1) for _ in range(N+1)]
print(dp)
# %%
for i in range(1, N+1):
for j in range(1, W+1): # bagsize
if wt[i-1] > j:
dp[i][j] = dp[i-1][j] # 当前容量装不下, 转而继承上一层,同一列的maxvalue
else:
value = val[i-1] + dp[i-1][j-wt[i-1]]
dp[i][j] = max(value, dp[i-1][j])
print(dp[N][W])
# 输入描述:
# N W : 物品数量, 背包大小
# wt: n个数,表示每个物品的重量
# val: n个数,表示每个物品的价值
N, W = 4,4
wt = [4,3,1,1]
val = [3000, 2000, 1500, 2000]
dp = [[0]*(W + 1) for _ in range(N+1)]
for id in range(1, N+1):
for bagsize in range(1,W+1):
if bagsize < wt[id - 1]:
dp[id][bagsize] = dp[id-1][bagsize]
else:
# 在背包大小维度单调非减,在物品维度需要比较
value = val[id - 1] + dp[id-1][bagsize - wt[id - 1]]
dp[id][bagsize] = max(dp[id-1][bagsize], value)
print(dp[N][W])
i,j 的位置换了
和上面的恰好相反
N, W = 4,4
wt = [4,3,1,1]
val = [3000, 2000, 1500, 2000 ]
# import sys
# nwStr = sys.stdin.readline().strip()
# N, W = list(map(int, nwStr.split()))
# wt = list(map(int, sys.stdin.readline().split()))
# val = list(map(int, sys.stdin.readline().split()))
length = len(wt)
dp = [[0]*(length+1) for _ in range(W+1)]
#P = [[False] * (length+1) for _ in range(W+1)]
for j in range(1, length+1):
for i in range(1, W+1):
bagSize = i
if bagSize < wt[j-1]:
dp[i][j] = dp[i][j-1]
else:
chose = val[j-1]+dp[i-wt[j-1]][j-1]
# 沿着背包容量的维度,是单调非减得
dp[i][j] = max(dp[i][j-1], chose) # 沿着wt维度, 需要比较
# if chose > dp[i][j-1]:
# P[i][j] = True
print(dp[W][length]) # 返回背包装入的最大价值
# 输入描述:
# N W : 物品数量, 背包大小
# wt: n个数,表示每个物品的重量
# val: n个数,表示每个物品的价值
#输入:
# 4 4
# 4 3 1 1
# 3000 2000 1500 2000
# 输出:
# 4000
状态压缩,滚动数组,内层先遍历包的大小
# 输入描述:
# N W : 物品数量, 背包大小
# wt: n个数,表示每个物品的重量
# val: n个数,表示每个物品的价值
N, W = 4,4
wt = [4,3,1,1]
val = [3000, 2000, 1500, 2000]
dp = [[0]*(W+1) for _ in range(2)]
for i in range(1, N+1):
for bagsize in range(1, W+1):
if bagsize >= wt[i-1]:
value = val[i-1] + dp[(i-1)%2][bagsize - wt[i-1]]
dp[i%2][bagsize] = max(dp[(i-1)%2][bagsize], value)
else:
dp[i%2][bagsize] = dp[(i-1)%2][bagsize]
print(dp[N%2][W])
带选择模式的信息
返回背包能装入的最大价值
另外可获得一条最佳路线
N, W = 4,4
wt = [4,3,1,1]
val = [3000, 2000, 1500, 2000 ]
# import sys
# nwStr = sys.stdin.readline().strip()
# N, W = list(map(int, nwStr.split()))
# wt = list(map(int, sys.stdin.readline().split()))
# val = list(map(int, sys.stdin.readline().split()))
length = len(wt)
dp = [[0]*(length+1) for _ in range(W+1)]
P = [[False] * (length+1) for _ in range(W+1)]
for j in range(1, length+1):
for i in range(1, W+1):
bagSize = i
if bagSize < wt[j-1]:
dp[i][j] = dp[i][j-1]
else:
chose = val[j-1]+dp[i-wt[j-1]][j-1]
dp[i][j] = max(dp[i][j-1], chose)
if chose > dp[i][j-1]:
P[i][j] = True
print(dp[W][length]) # 返回背包装入的最大价值
# 回溯,返回选择了那些物品
list1 = []
i = W
j = length
while i > 0 and j > 0:
col = j-1
if P[i][j]:
#print("i,j ", i, j)
list1.append((wt[col], val[col]))
i -= wt[col]
j -= 1
print(list1)
# 输入描述:
# N W : 物品数量, 背包大小
# wt: n个数,表示每个物品的重量
# val: n个数,表示每个物品的价值
#输入:
# 4 4
# 4 3 1 1
# 3000 2000 1500 2000
# 输出:
# 4000
# [(1, 2000), (3, 2000)]