小尼写的这篇page是先对0-1背包上一篇的二维数组求解做一个优化,小尼在这里优化成一维数组进行对应的求解。我们发现可以把dp[i-1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);我们看到这一个思路,我们其实可以想到我们为什么不直接用一个一维数组来表示:dp[j]即可,这个数组小尼在这里也是称为滚动数组,滚动的由来就是我们满足上一层的条件,我们可以进行重复的利用,并且直接拷贝到当前层。
小尼继续来给各位分析一下动规五部曲:
第一步:确定dp数组的定义
在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]
第二步:一维dp数组的递推公式
其实我们递推公式的推导也是从两个方向进行对应的推到,我们也只有两种进行的方式,要么就是放入i,要么就是不放入。一个是取自己dp[i]相当于二位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[0] = 0,因为背包容量为0所背的物品的最大的价值就是0.
第四步:一维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]);
}
}
这边就不像二维遍历一样了,这里遍历的顺序是需要先遍历物品,然后再遍历背包的容量,我们再这里跟二维遍历就不一样,小尼也在这里跟大家说明一下原因,我们这里的第二层for循环的遍历是从bagWeight开始的,原因是我们需要保证每一件物品一定只能取出一次,一旦我们从头开始进行对应的遍历,我们的物品就会取多次,所以我们采取从后遍历的顺序进行
第五步:最后只需要导出我们的dp数组即可
小尼接下来来一段一维数组可以遍历的例代码:
public static void main(String[] args) {
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWight = 4;
testWeightBagProblem(weight, value, bagWight);
}
public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){
int wLen = weight.length;
//定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
int[] dp = new int[bagWeight + 1];
//遍历顺序:先遍历物品,再遍历背包容量
for (int i = 0; i < wLen; i++){
for (int j = bagWeight; j >= weight[i]; j--){
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
//打印dp数组
for (int j = 0; j <= bagWeight; j++){
System.out.print(dp[j] + " ");
}
}
希望可以帮助到小伙伴们~~~