来源: https://www.acwing.com/problem/content/3/
推荐视频讲解: https://www.acwing.com/video/324/
题目描述:
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
PS: 完全背包和01背包的区别就是01背包只能选一件,完全背包可以选无数件
闫氏DP法:

状态计算就是状态方程,所以: $ f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i], f[i - 1][j - 2v[i] + 2w[i],…
, f[i - 1][j - k*v[i] + k * w[i]) $ 这里$j - K * v[i] >= 0$
伪代码如下:
1 2 3 4 5
| for i <- 1 to n: for j <- 1 to m: for k <- 0 to j - k*v[i] > 0: f[i][j] = max(f[i - 1][j], f[i - 1][j - k * v[i]] + k * w[i])
|
Python 实现如下(超时):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| N = 1010 dp, v, w = [[0]*N for _ in range(N)], [0]*N, [0]*N
def main(): n, m = map(int, input().split()) for i in range(1, n + 1): v[i], w[i] = map(int, input().split())
for i in range(1, n + 1): for j in range(1, m + 1): k = 0 while k * v[i] <= j: dp[i][j] = max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]) k += 1
print(dp[n][m])
main()
|
在此进行优化一下:
$f[i][j] = max(f[i][j], f[i- 1][j – v] + w, f[i - 1][j - 2v] + 2w, … f[i - 1][j - kv] + kw)$
$f[i][j - v] = max(f[i][j - v], f[i- 1][j – 2v] + w, f[i - 1][j - 3v] + 2w, … f[i - 1][j - kv] + (k - 1)w)$

这里的运算是取最大值, 所以红框里面部分只相差一个w[i] 所以状态方程就可以写成为:
$f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])$
01背包状态方程式:
$f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i])$
不同点在于01背包是i - 1, 而完全背包是i
所以伪代码如下(类似于01背包):
1 2 3 4 5 6
| for i <- 1 to n: for j <- 1 to m: f[i][j] = f[i - 1][j] if v[i] <= j: f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]
|
Python实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| N = 1010 dp = [[0]*N for _ in range(N)] w = [0]*N v = [0]*N
def main(): n, m = map(int, input().split()) for i in range(1, n + 1): v[i], w[i] = map(int, input().split())
for i in range(1, n + 1): for j in range(1, m + 1): dp[i][j] = dp[i - 1][j] if v[i] <= j: dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i])
print(dp[n][m])
main()
|
同样也可以优化为一维,由于是f[i][j]需要的是f[i][j - v[i]]的状态,所以,只需要在使用前计算更新计算过第i层就行了。只要从小到大开始枚举,那么所在层数的j - v[i]是更新过了的,也就是第i层
伪代码如下:
1 2 3
| for i <- 1 to n: for j <- v[i] to m: f[j] = max(f[j], f[j - v[i]] + w[i]
|
代码和01背包的写法非常类似,只是循环一个是从前,一个是从后。
Python实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| N = 1010 dp, w, v = [0]*N, [0]*N, [0]*N
def main(): n, m = map(int, input().split()) for i in range(1, n + 1): v[i], w[i] = map(int, input().split())
for i in range(1, n + 1): for j in range(v[i], m + 1): dp[j] = max(dp[j], dp[j - v[i]] + w[i])
print(dp[m])
main()
|