今天是背包问题的第一天,首先对背包问题的原理进行理解:
0-1背包:
问题描述:有n件物品和一个最多能背重量w的背包,第i件的重量是weight[ i ],得到的价值是value[ i ]。每件物品只能用一次,求解将那些物品装入背包里物品总价值最大
此时定义的dp数组dp[ i ][ j ]表示的是在第i件物品和总重量是j的情况下的最大价值。
也就是一个二维数组,而推动这个数组向前走的递推公式则取决于对于i物品的取舍,也就是是否将物品i放入背包中
放入背包中:则是dp[ i ][ j ] = dp[i - 1][j - weight[i]] + value[i]
不放入背包中:dp[ i ][ j ] = dp[ i-1][ j ]
判断是否放入背包中则是由背包的容量所决定的
递推公式为dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
!!!
这里我陷入了一个思想误区,那就是当第一个以及放进去了,但是第一个不是最优解时任何把它拿出来的问题:
其实理论上已经解决了,如果我总的重量是3的话到物品1那一行任何会与上一行进行取最大值的操作,此时物品一这一行的重量3位就会换为20此时背包里只要物品1.
!!!!!以下为二维转一维的dp数组
有点没搞懂,递推公式如下所示:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
我懂了,这里是去找,之前除去要放的物品重量的最大价值处取值,任何加上现在的物品价值,再与没有放此物品时的最大价值相比较,取一个最大的即可。
还有一个细节就是要先物品再背包
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]);
}
}
这样可以保证每个物品都只被添加了一次。
416. 分割等和子集
今天先上代码:
public boolean canPartition(int[] nums) {
int sum = 0;
for(int i:nums){
sum+=i;
}
if(sum%2!=0)return false;
sum/=2;
int[] dp = new int[sum+1];
for(int i=0;i<nums.length;i++){
for(int j=sum;j>=nums[i];j--){
dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);
if(dp[j]==sum)return true;
}
}
return false;
}
解析:首先可以将这个数组切分为两个数组则说明数组内的内容和必定是偶数,先进行剪枝操作,然后算出背包的最大容量,即num数组和的一半,当塞满这个背包时则返回true,否则else,这里使用的是背包问题的滚动数组的递推公式,细节nums数组里的数既是重量也是价值