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是背包最大容量

被折叠的 条评论
为什么被折叠?



