背包问题
0-1 背包问题
给定一个可装载重量为
W
的背包和N
个物品,每个物品有重量和价值两个属性。其中第i
个物品的重量为wt[i]
,价值为val[i]
,现在让你用这个背包装物品,最多能装的价值是多少?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQFOXKce-1641524249049)(%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98.assets/1.png)]
举个简单的例子,输入如下:
N = 3, W = 4
wt = [2, 1, 3]
val = [4, 2, 3]
算法返回 6,选择前两件物品装进背包,总重量 3 小于 W
,可以获得最大价值 6。
题目就是这么简单,一个典型的动态规划问题。这个题目中的物品不可以分割,要么装进包里,要么不装,不能说切成两块装一半。这就是 0-1 背包这个名词的来历。
解决这个问题没有什么排序之类巧妙的方法,只能穷举所有可能,根据我们 动态规划详解 中的套路,直接走流程就行了。
0-1背包问题模板
dp
数组的定义:
dp[i][w]
表示:对于前 i
个物品,当前背包的容量为 w
时,这种情况下可以装下的最大价值是 dp[i][w]
。
如果你没有把这第 i
个物品装入背包,那么很显然,最大价值 dp[i][w]
应该等于 dp[i-1][w]
,继承之前的结果。
如果你把这第 i
个物品装入了背包,那么 dp[i][w]
应该等于 dp[i-1][w - wt[i-1]] + val[i-1]
。
首先,由于 i
是从 1 开始的,所以 val
和 wt
的索引是 i-1
时表示第 i
个物品的价值和重量。
而 dp[i-1][w - wt[i-1]]
也很好理解:你如果装了第 i
个物品,就要寻求剩余重量 w - wt[i-1]
限制下的最大价值,加上第 i
个物品的价值 val[i-1]
。
综上就是两种选择,我们都已经分析完毕,也就是写出来了状态转移方程,可以进一步细化代码:
注意为什么i不从0开始
//01背包
for (int i = 1; i <= N; i++) {
for (int w = 1; w <= W; w++) {
//w可以从nums[i]开始
if (w - wt[i-1] < 0) {
// 这种情况下只能选择不装入背包
dp[i][w] = dp[i - 1][w];
} else {
// 装入或者不装入背包,择优
dp[i][w] = max(dp[i -1][w - wt[i-1]] + val[i-1],
dp[i - 1][w]);
}
}
}
return dp[N][W]
完全背包问题
我们可以把这个问题转化为背包问题的描述形式:
有一个背包,最大容量为 amount
,有一系列物品 coins
,每个物品的重量为 coins[i]
,每个物品的数量无限。请问有多少种方法,能够把背包恰好装满?
与0-1的区别在于每个物品数量无限
题解思路
第一步要明确两点,「状态」和「选择」。
状态有两个,就是「背包的容量」和「可选择的物品」,选择就是「装进背包」或者「不装进背包」嘛,背包问题的套路都是这样。
明白了状态和选择,动态规划问题基本上就解决了,只要往这个框架套就完事儿了:
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态