来源: https://www.acwing.com/problem/content/2/
推荐视频讲解: https://www.acwing.com/video/322/
题目描述:
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
根据闫氏DP法:

状态计算就是状态方程,所以: $ f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i])$ 这里$j - v[i] >= 0 $
所以伪代码如下:
1 2 3 4 5 6 7
| for i <- 0 to n: for j <- 0 to m: f[i][j] = f[i - 1][j] if v[i] <= j: f[i][j] = max(f[i][j], f[i - 1][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)] v = [0]*N w = [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 j >= v[i]: dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i])
print(dp[n][m])
main()
|
根据dp表格所推出来的数据如下, 行表示物品i, 列表示背包容量v
0 |
1 |
2 |
3 |
4 |
5 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
2 |
2 |
2 |
2 |
0 |
2 |
4 |
6 |
6 |
6 |
0 |
2 |
4 |
6 |
6 |
8 |
0 |
2 |
4 |
6 |
6 |
8 |
由状态转移方程可以看出,f[i][j]都是来自于第i-1层的数据,这时就可以使用可以利用一层空间进行状态的记录,我们只需要从大到小的枚举, 那么$f[j - v[i]]$的状态就是$f[i - 1][j - v[i]]$的状态。
伪代码如下:
1 2 3 4 5
| for i <- 1 to n: for j <- v to v[i]: f[j] = max(f[j], f[j - v[i] + w[i])
|
因为使用一维状态,那么f[j] = f[i - 1][j]保持着上次的数据,这时就只需要枚举到v[i]。但是在二维背包中,f[i][j]默认是0,每层的背包都需要跟新,所以背包容量j从0开始枚举到m。
具体Python代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| N = 1010 dp, v, w = [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(m, v[i] - 1, -1): dp[j] = max(dp[j], dp[j - v[i]] + w[i])
print(dp[m])
main()
|