营销型网站建设集装箱液袋,做文案应该关注的网站推荐,网站备案怎么登陆,桂林 网站建站01背包问题基础
问题描述
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]#xff0c;得到的价值是value[i] 。每件物品只能用一次#xff0c;求解将哪些物品装入背包里物品价值总和最大。
举个栗子
背包最大重量为4。 物品为#xff1a;
重量价值…01背包问题基础
问题描述
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]得到的价值是value[i] 。每件物品只能用一次求解将哪些物品装入背包里物品价值总和最大。
举个栗子
背包最大重量为4。 物品为
重量价值物品0115物品1320物品2430
二维dp数组01背包
动归五部曲
1.确定dp数组以及下标的含义
对于背包问题有一种写法 是使用二维数组即dp[i][j] 表示从下标为[0-i]的物品里任意取放进容量为j的背包价值总和最大是多少。
2.确定递推公式
可以有两个方向推出来dp[i][j]
不放物品i由dp[i - 1][j]推出即背包容量为j里面不放物品i的最大价值此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时物品i无法放进背包中所以被背包内的价值依然和前面相同。)放物品i由dp[i - 1][j - weight[i]]推出dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值那么dp[i - 1][j - weight[i]] value[i] 物品i的价值就是背包放物品i得到的最大价值
所以递归公式 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i])
3.dp数组如何初始化
首先从dp[i][j]的定义出发如果背包容量j为0的话即dp[i][0]无论是选取哪些物品背包价值总和一定为0。如图 其次是其他情况。状态转移方程 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); 可以看出i 是由 i-1 推导出来那么i为0的时候就一定要初始化。 dp[0][j]即i为0存放编号0的物品的时候各个容量的背包所能存放的最大价值。 那么很明显当 j weight[0]的时候dp[0][j] 应该是 0因为背包容量比编号0的物品重量还小。 当j weight[0]时dp[0][j] 应该是value[0]因为背包容量放足够放编号0物品。 代码初始化如下
for (int j 0 ; j weight[0]; j) { // 当然这一步如果把dp数组预先初始化为0了这一步就可以省略但很多同学应该没有想清楚这一点。dp[0][j] 0;
}
// 正序遍历
for (int j weight[0]; j bagweight; j) {dp[0][j] value[0];
}此时dp数组初始化情况如图所示 dp[0][j] 和 dp[i][0] 都已经初始化了那么其他下标应该初始化多少呢
其实从递归公式 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); 可以看出dp[i][j] 是由左上方数值推导出来了那么 其他下标初始为什么数值都可以因为都会被覆盖。 所以为了方便可以初始化为0。 最后初始化代码如下
// 初始化 dp
vectorvectorint dp(weight.size(), vectorint(bagweight 1, 0));
for (int j weight[0]; j bagweight; j) {dp[0][j] value[0];
}4.确定遍历顺序
先遍历 物品还是先遍历背包重量呢其实都可以但是先遍历物品更好理解。 那么我先给出先遍历物品然后遍历背包重量的代码。
// weight数组的大小 就是物品个数
for(int i 1; i weight.size(); i) { // 遍历物品for(int j 0; j bagweight; j) { // 遍历背包容量if (j weight[i]) dp[i][j] dp[i - 1][j]; else dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]);}
}5.举例推导dp数组
对应的dp数组的数值如图
void test_2_wei_bag_problem1() {vectorint weight {1, 3, 4};vectorint value {15, 20, 30};int bagweight 4;// 二维数组vectorvectorint dp(weight.size(), vectorint(bagweight 1, 0));// 初始化for (int j weight[0]; j bagweight; j) {dp[0][j] value[0];}// weight数组的大小 就是物品个数for(int i 1; i weight.size(); i) { // 遍历物品for(int j 0; j bagweight; j) { // 遍历背包容量if (j weight[i]) dp[i][j] dp[i - 1][j];else dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]);}}cout dp[weight.size() - 1][bagweight] endl;
}int main() {test_2_wei_bag_problem1();
}
01背包问题之滚动数组二维转化成一维
动规五部曲分析如下
1.确定dp数组的定义
在一维dp数组中dp[j]表示容量为j的背包所背的物品价值可以最大为dp[j]。
2.一维dp数组的递推公式
dp[j]为 容量为j的背包所背的最大价值那么如何推导dp[j]呢
dp[j]可以通过dp[j - weight[i]]推导出来dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。
dp[j - weight[i]] value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。也就是容量为j的背包放入物品i了之后的价值即dp[j]
此时dp[j]有两个选择一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j]即不放物品i一个是取dp[j - weight[i]] value[i]即放物品i指定是取最大的毕竟是求最大价值
所以递归公式为
dp[j] max(dp[j], dp[j - weight[i]] value[i]);
可以看出相对于二维dp数组的写法就是把dp[i][j]中i的维度去掉了。
3.一维dp数组如何初始化
dp[j]表示容量为j的背包所背的物品价值可以最大为dp[j]那么dp[0]就应该是0因为背包容量为0所背的物品的最大价值就是0。
那么dp数组除了下标0的位置初始为0其他下标应该初始化多少呢
看一下递归公式dp[j] max(dp[j], dp[j - weight[i]] value[i]);
dp数组在推导的时候一定是取价值最大的数如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。
这样才能让dp数组在递归公式的过程中取的最大的价值而不是被初始值覆盖了。
那么我假设物品价值都是大于0的所以dp数组初始化的时候都初始为0就可以了。
4.一维dp数组遍历顺序
代码如下
for(int i 0; i weight.size(); i) { // 遍历物品for(int j bagWeight; j weight[i]; j--) { // 遍历背包容量dp[j] max(dp[j], dp[j - weight[i]] value[i]);}
}这里大家发现和二维dp的写法中遍历背包的顺序是不一样的
二维dp遍历的时候背包容量是从小到大而一维dp遍历的时候背包是从大到小。
为什么呢
倒序遍历是为了保证物品i只被放入一次。但如果一旦正序遍历了那么物品0就会被重复加入多次
举一个例子物品0的重量weight[0] 1价值value[0] 15
如果正序遍历
dp[1] dp[1 - weight[0]] value[0] 15dp[2] dp[2 - weight[0]] value[0] 30此时dp[2]就已经是30了意味着物品0被放入了两次所以不能正序遍历。
为什么倒序遍历就可以保证物品只放入一次呢
倒序就是先算dp[2]
dp[2] dp[2 - weight[0]] value[0] 15 dp数组已经都初始化为0dp[1] dp[1 - weight[0]] value[0] 15所以从后往前循环每次取得状态不会和之前取得状态重合这样每种物品就只取一次了。
那么问题又来了为什么二维dp数组历的时候不用倒序呢
因为对于二维dpdp[i][j]都是通过上一层即dp[i - 1][j]计算而来本层的dp[i][j]并不会被覆盖
再来看看两个嵌套for循环的顺序代码中是先遍历物品嵌套遍历背包容量那可不可以先遍历背包容量嵌套遍历物品呢
不可以
因为一维dp的写法背包容量一定是要倒序遍历原因上面已经讲了如果遍历背包容量放在上一层那么每个dp[j]就只会放入一个物品即背包里只放入了一个物品。
倒序遍历的原因是本质上还是一个对二维数组的遍历并且右下角的值依赖上一层左上角的值因此需要保证左边的值仍然是上一层的从右向左覆盖。
所以一维dp数组的背包在遍历顺序上和二维其实是有很大差异的这一点大家一定要注意。
5.举例推导dp数组
一维dp分别用物品0物品1物品2 来遍历背包最终得到结果如下
void test_1_wei_bag_problem() {vectorint weight {1, 3, 4};vectorint value {15, 20, 30};int bagWeight 4;// 初始化vectorint dp(bagWeight 1, 0);for(int i 0; i weight.size(); i) { // 遍历物品for(int j bagWeight; j weight[i]; j--) { // 遍历背包容量dp[j] max(dp[j], dp[j - weight[i]] value[i]);}}cout dp[bagWeight] endl;
}int main() {test_1_wei_bag_problem();
}