代码随想录训练营第35天 || 01背包问题 416. 分割等和子集
01背包问题
就是将一些有价值的物品(只能放一次),放入有限容量的背包里,怎么放价值最大。
二维数组方法
动规五部曲:
1.dp数组及下标含义
dp[ i ][ j ]
dp数组表示当前背包容量,当前可放入物品下,最大的背包价值,下标的含义:i是物品,j是背包的递推容量(为什么叫递推容量,因为背包的容量是一点一点在扩大,来推出最终背包的总容量的最大价值),当然也可以颠倒。
2.递推公式:
dp[ i ][ j ] = max( dp[ i-1 ][ j ] ,dp[ i-1 ][j-weight[ i ](表示i物品的重量)+value[i] )
3. dp数组如何初始化
把最左边一列和最上面一行初始化,因为其他的数组元素都是由上一个元素和左上的元素推出来的
左边一列为0,因为此时背包容量为0,上面一列,只能放物品0,要么是0,要么最大就是物品0的价值
4.确定遍历顺序:
遍历顺序:任意顺序都可
5.例举
总结:为什么求一个总背包容量的最大价值要拆分成二维数组,因为最终结果是由小背包容量推出来的
滚动数组方法:
1.dp数组及下标含义
dp [ j ] : j 是为了延续二维数组的方法j的含义,j是背包的容量
2.递推公式:
dp[ j ] = max (dp [ j ] , dp[ j -weight[ i ] ] + value[i] ]
与二维数组不同的是,数组是重复利用的,所有当前数组如果不修改就等效为二维数组时上一层的数组
3. dp数组如何初始化
4.确定遍历顺序:
遍历顺序只能先遍历物品,再遍历背包容量,并且遍历背包容量只能倒序(倒序遍历是为了保证物品i只被放入一次)
5.例举
416. 分割等和子集
思路:
01背包的应用:因为要分割成两个等和的子集,所以把一半的和想象成背包,只要在数组中找到能够装满这个背包的子集就行
代码:
class Solution {
public:bool canPartition(vector<int>& nums) {int sum = 0;vector<int> dp(10001,0);//dp数组的初始化for(int i = 0;i<nums.size();i++)//求和{sum+=nums[i];}if(sum%2==1)return false;//总和必须能一分为二,否则不能一分为二int target = sum/2;//target相当于背包问题的背包总容量for(int i =0;i<nums.size();i++){for(int j = target;j >= nums[i];j--)//为什么从target开始遍历,因为target是背包最大容量{dp[j] =max(dp[j] , dp[j-nums[i]]+nums[i]);//max中的dp[j]是不放入dp当前i物品也就是num[i],dp[j-nums[i]]+nums[i]是放入nums[i]}}if (dp[target] == target)return true;return false;}
};
遇到的问题:
1.对于总和的一半想象成背包总重量的背包问题,并且每个数字本身代表价值和重量
2. for(int j = target;j >= nums[i];j--)//为什么从target开始遍历,因为target是背包最大容量