0-1背包问题分析_代码编写

目录

​​概念​​

​​步骤​​

​​数组转化​​

​​遍历顺序​​

​​代码编写​​


概念

W:背包最大总重量

Y:当前最大总重量限制(遍历时会变动:0~W)

N:物品的总数量

w_j:物品 的重量(j=0~N)

p_j:物品 的价值

A(j, Y):总重量不超过Y的前提下,前 个物品的最高价值

\\ A(0, Y)=0 \\ w_j> Y: A(j, Y)=A(j-1, Y)) \\ w_j\leq Y: A(j, Y)=max(A(j-1, Y), p_j+A(j-1, Y-w_j))

步骤

单重+单价\总重Y

0

1

5

9

0,0

1,2

4,3

5,4

对于j=0,A(0, Y) = 0;

对于Y=0,A(Y,0) = 0。

单重+单价\总重Y

0

1

5

9

0,0

0

0

0

0

2,2

0

4,3

0

5,4

0

对于j=1,wj=2,pj=2,Y=1时,有wj > Y,因此放不下,所以沿用前(j-1)个时的价值,而Y不变。

w_j> Y: A(j, Y)=A(j-1, Y))

单重+单价\总重Y

0

1

5

9

0,0

0

0

0

0

2,2

0

0

4,3

0

5,4

0

0-1背包问题分析_数组_04

有可能放入后的总价值,还不如不放入的高。

比如:总重量5,已经放入了(4,5),现在要放进去(2,1),挤出去(2,2),那总重量更新为(4,4),总价值变得更小了。

0560,00002,302,32,204,52,104,4??

对于j=1,wj=2,pj=2,Y=5时,有wj < Y,因此可以放下。

A(j-1,Y):不放入当前物品时的最大价值。

A(j-1,Y-wj):对于前j-1个物品,最大值重量为Y-wj时,看它能存的最大总价值是多少。

pj+A(j-1,Y-wj):即先看去掉当前物品重量后,能存的最大价值,再加上当前物品的价值。

单重+单价\总重Y

0

1

5

9

0,0

0

0

0

0

2,2

0

0

2,2

2,2

4,3

0

0

4,3

6,5

5,4

0

0

5,4

9,7

数组转化

A(i,Y) => dp[i][j]

前 个物品,在最大重量限制为 的情况下,最大价值为 dp[i][j]

  • 由于存在0行0列,因此长度需要额外+1
  • 重量长度 = 背包总重+1
  • 物品长度 = 物品总数+1
  • 根据递推公式,当前 dp[i][j] 上方左上角的内容,推算而来。
  • 最后结果只需要返回dp右下角的值即可。

遍历顺序

两层for循环:

  • 第一层(外层):遍历物品
  • 第二层(内层):遍历背包
  • 备注:对于当前的二维数组,顺序可颠倒。
for i in range(1, N+1):
for j in range(1, W+1):
... ...

代码编写

class Solution:
def package(self, N, W, weights, values):
# 创建数组并初始化
dp = [[0 for _ in range(W+1)] for _ in range(N+1)]
# 遍历物品
for i in range(1, N+1):
# 当前物品重量
pi = values[i-1]
# 当前物品价值
wi = weights[i-1]
# 遍历背包
for j in range(1, W+1):
# 放不下的情况
if wi > j:
# 沿用前面的价值
dp[i][j] = dp[i-1][j]
# 放得下的情况
else:
# 取"放"和"不放"两者的较大值
dp[i][j] = max(dp[i-1][j], pi+dp[i-1][j-wi])
for i in dp: print(i)
# 输出结果
print(dp[-1][-1])
[0, 0, 0, 0, 0]
[0, 0, 4, 4, 4]
[0, 2, 4, 6, 6]
[0, 2, 4, 6, 6]
6