动态规划 - 01背包
来源: https://www.acwing.com/problem/content/2/
题目描述:
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
动态规划分析分为:状态计算、状态表示
状态表示f[i][j]
:
- 集合: 前i个物品,取得体积不超过j的价值
- 属性: Max
状态计算:
- 以取第i件物品和不取第i件物品进行划分
- 不取第i件物品,那么
f[i][j] = f[i - 1][j - 1]
- 取第i件物品,等于减去当前物品的容量,加上当前物品的价值。
f[i][j] = f[i][j - v[i]] + w[i]
所以伪代码如下:
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代码实现如下:
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]]
的状态。
伪代码如下:
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代码如下:
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()