经典01背包问题 + 状态回溯选择

经典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)]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值