0-1背包问题是用一个有限容量的背包,尽可能地装下更多价值的东西,每个物品的数量都是1,以下面的数据为一个例子:
价值 | 重量 | |
物品1 | 15 | 1 |
物品2 | 20 | 2 |
物品3 | 30 | 4 |
物品4 | 40 | 5 |
如今你有一个容量为7的背包,请问如何装才能使你的装下的物品总价值最高?
这里我就不论证为什么这类问题要用动态规化来解决了,着重分析该问题如何使用动态规划来解决。
使用一个二维dp[ i ][ j ]数组来记录数据
背包容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
物品1 | ||||||||
物品2 | ||||||||
物品3 | ||||||||
物品4 |
我们以下标的形式分别标注行、列为第0行,第1行…… 第0列,第1列……
dp[ i ][ j ]内的 i 表示只装下标为[0,i]的物品,例如:i=2,表示只装下标为[0,2]的物品,就是只考虑物品1,物品2,物品3
dp[ i ][ j ]内的 j 表示背包容量,例如j=3,表示背包当前容量为3
dp[ i ][ j ]内每一个小单元格,记录只考虑下标为[0, i]的物品,在当前背包容量为j时能装下物品的最大价值,例如:dp[ 2 ][ 3 ]记录只考虑物品1,物品2,物品3时,在背包容量为3时,背包内装下物品的最大价值
解释完dp数组的基本功能后,接下来就是dp数组的初始化,
dp数组的初始化
显而易见的,当背包容量j=0时,背包内装下物品的最大价值都为0,
当背包容量j>0时,只装物品1,背包内装下物品的最大价值都为15,
因此我们就可以初始化我们的表格为:
背包容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
物品1 | 0 | 15 | 15 | 15 | 15 | 15 | 15 | 15 |
物品2 | 0 | |||||||
物品3 | 0 | |||||||
物品4 | 0 |
初始化完成后,就是根据递推关系填写表格
根据递推关系填写表格
dp[ i ][ j ]有两个递推方向
第一个,当 j < weight[ i ]时,即背包容量比物品i的容量还小,即使我们把背包里的东西都拿出来了,还是放不进背包里,现在考虑不考虑物品i结果都是一样的,背包内装下物品的最大价值就可以直接等于不考虑物品i时的最大价值
那么此时dp[ i ][ j ] = dp[ i - 1][ j ]
第二个,当j > weight[ i ]时,这时就可以放得下物品i了
但此时有会出现两种情况了,因为背包容量+1,+1,这样慢慢加起来的,如果只有+1,你不能直接把物品i装进背包里
这时你有两个选择了
选择1:背包容量就加了一点点,不能直接放进物品i,那还不如不管它 dp[ i ][ j ] = dp[ i - 1][ j ]
选择2:你把原来背包中的物品拿出来一部分,空出的位置装下物品i,这样 dp[ i ][ j ] = dp[ i-1 ][ j - weight( i )] + value( i ),就是将背包空出足够空间装物品i的情况,空出后的最大价值 + 物品 i 的价值 = 当前j容量下背包的最大价值
比较上面两种选择的最大价值,选择最大的价值就可以
以下是代码实例:
void test02()
{
vector<int> value = { 15,20,30,40 }; //记录物品的价值
vector<int> weight = { 1,2,4,5 }; //记录物品的重量
int bagWeight = 7; //背包容量
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
//初始化dp数组
//当背包容量j=0时,背包内装下物品的最大价值都为0,
for (int i = 0; i < weight.size(); i++)
{
dp[i][0] = 0;
}
//当背包容量j>0时,只装物品1,背包内装下物品的最大价值都为物品1的价值
for (int i = 1; i <= bagWeight; i++)
{
dp[0][i] = value[0];
}
//遍历dp数组,填写表格
for (int i= 1; i < weight.size(); i++) { //遍历物品
for (int j = 1; j <= bagWeight; j++) { //遍历背包容量
/*
第一个,当 j < weight[ i ]时,即背包容量比物品i的容量还小
即使我们把背包里的东西都拿出来了,还是放不进背包里
现在考虑不考虑物品i结果都是一样的
背包内装下物品的最大价值就可以直接等于不考虑物品i时的最大价值
*/
if (j < weight[i])
{
dp[i][j] = dp[i - 1][j];
}
//第二个,当j > weight[ i ]时,这时就可以放得下物品i了
else
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
cout << dp[weight.size() - 1][bagWeight] << endl;
}
输出结果: