动态规划之背包问题——01背包
算法相关数据结构总结:
序号 | 数据结构 | 文章 |
---|---|---|
1 | 动态规划 |
动态规划之背包问题——01背包 动态规划之背包问题——完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 算法(Java)——动态规划 |
2 | 数组 | 算法分析之数组问题 |
3 | 链表 |
算法分析之链表问题 算法(Java)——链表 |
4 | 二叉树 |
算法分析之二叉树 算法分析之二叉树遍历 算法分析之二叉树常见问题 算法(Java)——二叉树 |
5 | 哈希表 |
算法分析之哈希表 算法(Java)——HashMap、HashSet、ArrayList |
6 | 字符串 |
算法分析之字符串 算法(Java)——字符串String |
7 | 栈和队列 |
算法分析之栈和队列 算法(Java)——栈、队列、堆 |
8 | 贪心算法 | 算法分析之贪心算法 |
9 | 回溯 |
Java实现回溯算法入门(排列+组合+子集) Java实现回溯算法进阶(搜索) |
10 | 二分查找 | 算法(Java)——二分法查找 |
11 | 双指针、滑动窗口 |
算法(Java)——双指针 算法分析之滑动窗口类问题 |
文章目录
- 一、01背包问题
- 二、二维dp数组解决01背包问题
- 1. 确定dp数组以及下标的含义
- 2. 确定递推公式
- 3. dp数组初始化
- 4. 确定遍历顺序
- 5. 举例推导dp数组
- 三、一维dp数组解决01背包问题
- 1. 确定dp数组以及下标的含义
- 2. 一维dp数组的递推公式
- 3. 一维dp数组如何初始化
- 4. 一维dp数组遍历顺序
- 5. 举例推导dp数组
- 四、leetcode例题讲解01背包问题
- 416. 分割等和子集
- 1049. 最后一块石头的重量 II
- 494. 目标和
- 474. 一和零
- 五、01背包问题总结
- 1. 动规五步分析法
- 2. 背包递推公式
- 3. 遍历顺序
背包问题中我们常见的就是 01背包和 完全背包。在leetcode的题库中主要就是这两种类型的题目。而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。所以背包问题的基础就是01背包问题。完全背包问题请参考 动态规划之背包问题——完全背包。
一、01背包问题
有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是O(2^n)。
所以需要动态规划来解题。
二、二维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];
}
一开始把数组统一初始化为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]);}
}
先遍历背包,再遍历物品:
// weight数组的大小 就是物品个数
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量for(int i = 1; i < weight.size(); i++) { // 遍历物品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]);}
}
为什么两种顺序都可以?要理解递归的本质和递推的方向。
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
递归公式中可以看出dp[i][j]
是靠dp[i-1][j]和dp[i - 1][j - weight[i]]
推导出来的。
dp[i-1][j]
和dp[i - 1][j - weight[i]]
都在dp[i][j]
的左上角方向(包括正上方向),
那么先遍历物品,再遍历背包的过程和先遍历背包,再遍历物品,dp[i][j]
所需要的数据就是左上角,根本不影响dp[i][j]
公式的推导。
5. 举例推导dp数组
最终结果就是dp[2][4]。
完整Java测试代码:
public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){//定义dp数组:dp[i][j]表示背包容量为j时,前i个物品能获得的最大价值int[][] dp = new int[wLen + 1][bagSize + 1];//初始化:背包容量为0时,能获得的价值都为0for (int j = weight[0]; j <= bagWeight; j++) {dp[0][j] = value[0];}//遍历顺序:先遍历物品,再遍历背包容量for (int i = 1; i <= weight.length; i++){for (int j = 1; j <= bagSize; j++){if (j < weight[i - 1]){dp[i][j] = dp[i - 1][j];}else{dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]);}}}}
三、一维dp数组解决01背包问题
把二维dp降为一维dp,即使用一维滚动数组。
在使用二维数组的时候,递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。
dp[i][j]
表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
1. 确定dp数组以及下标的含义
在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
2. 一维dp数组的递推公式
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]);
3. 一维dp数组如何初始化
dp[j]
表示:容量为j的背包,所背的物品价值可以最大为dp[j]
,那么dp[0]
就应该是0,因为背包容量为0所背的物品的最大价值就是0。
假设物品价值都是大于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遍历的时候,背包是从大到小:倒叙遍历是为了保证物品i只被放入一次!
一旦正序遍历,那么物品0就会被重复加入多次。
举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15
如果正序遍历
dp[1] = dp[1 - weight[0]] + value[0] = 15
dp[2] = dp[2 - weight[0]] + value[0] = 30
此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。
倒叙就是先算dp[2]
dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)
dp[1] = dp[1 - weight[0]] + value[0] = 15
所以从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。
但对于二维dp,dp[i][j]都是通过上一层即dp[i - 1][j]计算而来,本层的dp[i][j]并不会被覆盖!
两个嵌套for循环的顺序:先遍历物品嵌套遍历背包容量
因为一维dp的写法,背包容量一定是要倒序遍历,如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。
5. 举例推导dp数组
一维dp,分别用物品0,物品1,物品2 来遍历背包,最终得到结果如下:
完整Java测试代码:
public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){//定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值int[] dp = new int[bagWeight + 1];//遍历顺序:先遍历物品,再遍历背包容量for (int i = 0; i < weight.length; i++){for (int j = bagWeight; j >= weight[i]; j--){dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);}}}
可以看出,一维dp 的01背包,要比二维简洁的多!而且空间复杂度还降了一个数量级!
四、leetcode例题讲解01背包问题
416. 分割等和子集
leetcode题目链接:416. 分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例一:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例二:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
这道题目是要找是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
那么只要找到集合里能够出现 sum / 2 的子集总和,就算是可以分割成两个相同元素和子集了。
本题中我们要使用的是01背包,因为元素我们只能用一次。
首先,本题要求集合里能否出现总和为 sum / 2 的子集。那么来一一对应一下本题,看看背包问题如果来解决。
- 背包的体积为sum / 2
- 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
- 背包如何正好装满,说明找到了总和为 sum / 2 的子集。
- 背包中每一个元素是不可重复放入。
1.确定dp数组以及下标的含义
套到本题,dp[i]表示 背包总容量是i,最大可以凑成i的子集总和为dp[i]。
2.确定递推公式
本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]。
所以递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
3.dp数组如何初始化
题目中 只包含正整数的非空数组,所以非0下标的元素初始化为0就可以了。
4.确定遍历顺序
for(int i = 0; i < nums.size(); i++) {for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);}
}
5.举例推导dp数组
dp[i]的数值一定是小于等于i的。
如果dp[i] == i 说明,集合中的子集总和正好可以凑成总和i,理解这一点很重要。
用例1,输入[1,5,11,5] 为例,如图:
最后dp[11] == 11,说明可以将这个数组分割成两个子集,使得两个子集的元素和相等。
完整Java代码:
class Solution {public boolean canPartition(int[] nums) {if(nums == null || nums.length == 0) return false;int n = nums.length;int sum = 0;for(int num : nums){sum += num;}//总和为奇数,不能平分if(sum % 2 != 0) return false;int target = sum / 2;int[] dp = new int[target + 1];for(int i = 0; i < n; i++){for(int j = target; j >= nums[i]; j--){//物品 i 的重量是 nums[i],其价值也是 nums[i]dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]);}}return dp[target] == target;}
}
这道题目就是一道01背包应用类的题目,需要我们拆解题目,然后套入01背包的场景。
01背包相对于本题,主要要理解,题目中物品是nums[i],重量是nums[i],价值也是nums[i],背包体积是sum/2。
1049. 最后一块石头的重量 II
leetcode题目链接:1049. 最后一块石头的重量 II
有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
示例一:
输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
示例二:
输入:stones = [31,26,33,21,40]
输出:5
本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这就回到了上一题分割等和子集,就化解成01背包问题了。
背包体积为sum/2,物品的重量stones[i],物品的价值stones[i]。
1.确定dp数组以及下标的含义
dp[i] 表示容量为i的背包最多可以装dp[i]重的石头
2.确定递推公式
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
3.dp数组如何初始化
同上。
4.确定遍历顺序
同上,先遍历物品,然后倒序遍历背包。
完整Java代码:
class Solution {public int lastStoneWeightII(int[] stones) {int sum = 0;for(int stone : stones){sum += stone;}int target = sum / 2;int[] dp = new int[target+1];for(int i = 0; i < stones.length; i++){for(int j = target; j >= stones[i]; j--){dp[j] = Math.max(dp[j], dp[j-stones[i]] + stones[i]);}}return sum - 2 * dp[target];}
}
494. 目标和
leetcode题目链接:494. 目标和
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例一:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例二:
输入:nums = [1], target = 1
输出:1
本题其实是让数组中的数字分成添加正号的数字和与添加负号的数字和相等,就化解成01背包的组合问题。
计算相等的方法有几种,就是组合问题。
这里可以设添加负号的数字和为neg (也可以设正号的),总和为sum,则添加正号的数字和为sum - neg。
要求 (sum - neg ) - neg= target,则neg = (sum - target) / 2。
判断target的值和sum的值的大小,如果target大,没有方案,不能整除也没有方案,都是非负整数。
转化为背包体积为neg= (sum - target) / 2,重量和价值都为nums[i]
1.确定dp数组以及下标的含义
dp[j] 表示元素和为j时的表达式数目
2.确定递推公式
dp[j] += dp[j], dp[j-nums[i]]
求组合类问题的公式,都是类似这种。
3.dp数组如何初始化
从递归公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递归结果将都是0。
dp[0] = 1,理论上也很好解释,装满容量为0的背包,有1种方法,就是装0件物品。
4.确定遍历顺序
同上,先遍历物品,然后倒序遍历背包。
完整Java代码:
class Solution {public int findTargetSumWays(int[] nums, int target) {// 动态规划,一个正集,一个负集,和为target// 设元素和为sum,负号和为neg,正号和为sum-neg ,则(sum-neg)-neg = target, neg = (sum - target) / 2// 装满背包体积为neg,重量和价值为nums[i]的方法有几种// dp[j] 表示元素和为j时的表达式数目int sum = 0;for(int num : nums){sum += num;}int diff = sum - target; // 判断target的值和sum的值的大小,如果target大,没有方案,不能整除也没有方案,都是非负整数if(diff < 0 || diff % 2 != 0) return 0; int neg = diff / 2; // 添加负号集的和int[] dp = new int[neg+1];dp[0] = 1;for(int i = 0; i < nums.length; i++){for(int j = neg; j >= nums[i]; j--){dp[j] += dp[j - nums[i]]; // 组合问题}}return dp[neg];}
}
474. 一和零
leetcode题目链接:474. 一和零
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例一:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
示例二:
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。
这道题可以说是一个变形的01背包问题,本题中strs 数组里的元素就是物品,每个物品都是一个!
而m 和 n相当于是一个背包,两个维度的背包。而不同长度的字符串就是不同大小的待装物品。
1.确定dp数组以及下标的含义
dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
2.确定递推公式
dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。
dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
然后我们在遍历的过程中,取dp[i][j]的最大值。
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。
这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。
3.dp数组如何初始化
因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
4.确定遍历顺序
物品就是strs里的字符串,背包容量就是题目描述中的m和n。
所以背包倒序遍历的时候需要遍历两个维度。
完整Java代码:
class Solution {public int findMaxForm(String[] strs, int m, int n) {// 动态规划,01背包问题,背包有两个维度,一个m,一个n.// strs 数组里的元素就是物品,每个物品都是一个,不同长度的字符串都是待装物品// dp[i][j]表示最多有i个0,j个1的strs的最大子集长度// dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);int zeroNum, oneNum;int[][] dp = new int[m+1][n+1];for(String str : strs){ // 先统计每个字符串0的个数和1的个数,遍历物品zeroNum = 0;oneNum = 0;for(char c : str.toCharArray()){if(c == '0') zeroNum++;else oneNum++;}for(int i = m; i >= zeroNum; i--){ // 遍历背包容量从后往前,两个背包for(int j = n; j >= oneNum; j--){dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);}}}return dp[m][n];}
}
五、01背包问题总结
1. 动规五步分析法
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
2. 背包递推公式
问能否能装满背包(或者最多装多少):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
,对应题目如下:
416. 分割等和子集
1049. 最后一块石头的重量 II
问装满背包有几种方法:dp[j] += dp[j - nums[i]]
,对应题目如下:
494. 目标和
问背包装满最大价值(多维背包问题):dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
,对应题目如下:
474. 一和零
3. 遍历顺序
二维dp数组01背包先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。
一维dp数组01背包只能先遍历物品再遍历背包容量,且第二层for循环是从大到小遍历。
动态规划其它题型总结:
动态规划之背包问题——完全背包
动态规划之打家劫舍系列问题
动态规划之股票买卖系列问题
动态规划之子序列问题
参考:代码随想录:背包理论基础01背包
动态规划之背包问题——01背包相关推荐
- 动态规划之背包问题---01背包---完全背包---多重背包
本篇博客是基于Carl大佬的刷题笔记 (代码随想录) 进行总结的 另外加入了我自己的一些整理,特此记录,以防遗忘 几种在面试中常见的背包,其关系如下: 通过这个图,可以很清晰分清这几种常见背包之间的关 ...
- 动态规划之背包问题 01背包
什么是01背包? 有n件物品和一个最多能背重量为w 的背包.第i件物品的重量是weight[i],得到的价值是value[i] .每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大. 首先, ...
- 背包问题——01背包
背包问题--01背包 01背包作为动态规划(dynamic programing)中最基础的问题,需要我们彻底理解其中的原理,为以后解决更难的动态规划问题打下良好的基础. 这里拟定一个01背包问题: ...
- 动态规划总结(01背包 完全背包 多重背包)
动态规划总结(01背包 完全背包 多重背包) 一.学习资料 1.UVA DP 入门专题 2.夜深人静写算法(二) - 动态规划 3.算法之动态规划 4.什么是动态规划?动态规划的意义是什么? 5.01 ...
- C++ 背包问题——01背包
由于编辑器原因,01背包文章搬家了,想看到更好的01背包问题题解,请点击链接: C++背包问题--01背包_小天狼星_布莱克的博客-CSDN博客
- 背包问题——01背包/完全背包/多重背包
背包问题是典型的动态规划问题,本文将对典型的背包问题进行总结. 0-1背包问题 有 N 件物品和一个容量是 V 的背包.每件物品只能使用一次. 第 i 件物品的体积是 vi,价值是 wi. 求解将哪些 ...
- 动态规划经典题目——0-1背包
一.题目 有N件物品和一个容量为V的背包.第i件物品的重量是w[i],价值是v[i].求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大. 二.解题思路 动态规划解题思路可详见 ...
- 经典背包问题 01背包+完全背包+多重背包
01 背包 有n 种不同的物品,每个物品有两个属性,size 体积,value 价值,现在给一个容量为 w 的背包,问最多可带走多少价值的物品. int f[w+1]; //f[x] 表示背包容量为x ...
- C++背包问题——01背包
01背包问题 1.题目 2.基本思路 3.优化空间复杂度 4.初始化的细节问题 5.小结 6.代码 1.题目 有N件物品和一个容量为 V V V的背包.放入第i件物品耗费的空间是 C i Ci Ci, ...
最新文章
- 日志分析工具splunt
- mahout demo——本质上是基于Hadoop的分步式算法实现,比如多节点的数据合并,数据排序,网路通信的效率,节点宕机重算,数据分步式存储...
- cinder配置多ceph储存池[Ceph and Cinder multi-backend]
- Windows 记事本的 ANSI、Unicode、UTF-8 这三种编码模式有什么区别?
- 帆软《商业智能》书籍首发,国产BI行业独家,福利发售!
- ideadebug热更新_Spring Boot 在IDEA中debug时的hot deployment(热部署)
- Codeforces 837D 动态规划
- flex align-center:center多行垂直方向居中 align-items:center垂直方向单行居中
- 自定义刻度尺进度条总结
- 1688API接口系列,教你如何获取商品详情
- Java解压压缩加密文件zip
- easyexcel结合zip 导出压缩文件(包含多个excel)
- 《管理学》第九章 沟通
- 苹果手机编辑word_苹果手机更新后卡顿,关闭这两个开关立马恢复,设置之后差点泪崩...
- 数学建模PPT(三)
- Centos安装python3并设为默认
- 2020震撼的AE模版SDK
- 敏捷个人2012.3月份线下活动报道:活出我的价值
- Laplace(拉普拉斯)先验与L1正则化
- 小米申请注册多个 “铁蛋”商标