问题:有 n 种物品和一个容量是 c 的背包,每种物品都有无限件可用。
第 i 种物品的重量是 weight [ i ] ,价值是 value [ i ] 。
求解将哪些物品装入背包,可使这些物品的总重量不超过背包容量,且总价值最大。输出最大价值。
算法1:朴素写法(三重循环)。
创建二维数组 dp ,dp [ i ][ j ] 表示从前 i 个物品中选,在总重量不超过 j 的前提下,所能挑选的最大价值。
从第一件物品开始遍历,从容量为 0 开始遍历。因为每件物品可以使用的数量不限,所以不能像 0/1背包 一样在列状态转移方程时只减去一个当前物品的重量 weight [ i - 1 ] ,要减去 k 个(weight [ i - 1 ] 中使用 i - 1 是因为 weight 数组 和 value 数组 都是从下标 0 开始记录的,而 dp 数组 是从下标 1 开始的。可以按照自己习惯修改)。
代码:
for (int i = 1; i <= n; i++)
for (int j = 0; j <= c; j++)
for (int k = 0; k * weight[i-1] <= j; k++)
dp[i][j] = max(dp[i][j], dp[i - 1][j - weight[i-1] * k] + value[i-1] * k);
算法2:第一次优化。
和 0/1背包 二维dp 类似,当前背包容量为 i 时所得最大价值,必不小于背包容量为 i - 1 时的最大价值,故先令此时背包容量的最大价值为 dp[ i - 1 ][ j ],再判断是否能放的下每个不同的物品,如果能放下,则比较后取较大的值。
代码:
for (int i = 1; i <= n; i++)
for (int j = 0; j <= c; j++) {
dp[i][j] = dp[i - 1][j];// 把上一行复制过来
if (j >= weight[i - 1]) dp[i][j] = max(dp[i][j], dp[i][j - weight[i - 1]] + value[i - 1]);// 从左到右更新
}
算法2:第二次优化。
与 0/1背包 一维dp 类似,但是此处是顺序遍历。
对于完全背包,由于每种物品可以取无限次,我们希望每个物品能够被重复考虑。因此,我们采用正序遍历背包容量的方式(即从 weight [ i ] 到 c)。这样,在更新 dp [ j ] 的时候,dp[ j - weight [ i ] ] 总是表示未选择当前物品 i 时的最大价值,因此当前物品可以被多次加入背包中,只要不超过背包容量。
因为顺序从小到大考虑,已经让每个物品被重复多次的考虑了(dp [ i , j - weight ] + value [ i ] 已经多次重复考虑计算之前的物品,顺序时,只要有能放入的空间,就放入物品)。
代码:
for (int i = 1; i <= n; i++)
for (int j = weight[i - 1]; j <= c; j++)
dp1[j] = max(dp1[j], dp1[j - weight[i - 1]] + value[i - 1]);
完整代码:
#include<iostream>
#include<stdlib.h>
using namespace std;
int main() {
int weight[4] = { 3,5,2,8 }, value[4] = { 4,6,1,9 }, dp[4 + 1][20 + 1],dp1[20 + 1];// dp对应方法12,dp1对应方法3
//memset(dp, 0, sizeof(dp));// 初始化dp(方法12)
memset(dp1, 0, sizeof(dp1));// 初始化dp1(方法3)
int n = 4, c = 20;// 物品个数n,背包容量c
/*
// 方法1
for (int i = 1; i <= n; i++)
for (int j = 0; j <= c; j++)
for (int k = 0; k * weight[i-1] <= j; k++)
dp[i][j] = max(dp[i][j], dp[i - 1][j - weight[i-1] * k] + value[i-1] * k);
*/
/*
// 方法2 第一次优化
for (int i = 1; i <= n; i++)
for (int j = 0; j <= c; j++) {
dp[i][j] = dp[i - 1][j];// 把上一行复制过来
if (j >= weight[i - 1]) dp[i][j] = max(dp[i][j], dp[i][j - weight[i - 1]] + value[i - 1]);// 从左到右更新
}
*/
// 方法3 第二次优化
for (int i = 1; i <= n; i++)
for (int j = weight[i - 1]; j <= c; j++)
dp1[j] = max(dp1[j], dp1[j - weight[i - 1]] + value[i - 1]);
cout << dp1[c] << endl;
//cout << dp[n][c] << endl;
return 0;
}