在上篇讲述了01背包基础问题,但是代码思想任然存在可优化地方,比如我们可以从递推公式可以看出来:
原递推公式
当前承重量的背包最大容纳价值 = max(腾出新物品空位后的背包承重量最大价值 + 新物品的价值,不加入新物品的最大价值);
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
原递推思路
定义一个二维数组,每一列为从0-max解放的背包重量,每一行表示新解放的物品
当只能放物品1时,背包容量从0-max,每个容量对应的最大价值就为
0 1 2 3 4
15 0 15 15 15 15
20
30
当解放物品2,那么背包容量从0-max,每个容量对应的最大价值就为
0 1 2 3 4
15 0 15 15 15 15
20 0 15 15 20 35
30
当解放物品2,那么背包容量从0-max,每个容量对应的最大价值就为
0 1 2 3 4
15 0 15 15 15 15
20 0 15 15 20 35
30 0 15 15 20 35
问题
递推公式只用到了 用于记录结果的本层数组,以及已经记录了最大值的上层数组,而前面所有的已经记录的最大值在经历完一层递推之后就毫无作用,每一层的递推都是根据上一层结果从左向右完成。完成后上层数据便失去了意义。
解决方案
我们采用一维数组去解决这样的问题,每次使用该数组时,该数组记录的是上一层的结果。我们观察此前的推导数组可以发现,每一个数组元素的推导,都是由该元素正上方元素,与左上方一行某个元素+value值比较。所以每个元素的推导需要的值我们可以看做一个L型
值1 值2 值3 值4
目标
这个L型就是加粗的L型
要想知道目标最贵摆放法,就需要用值3 与 减去目标重量后的最大值 + 目标的值 做比较。现在我们使用一维数组,如果还是从左向右遍历,那么就可能出错。
前:
原值1 原值2 原值3 原值4
目标
后:
原值1 新值2 原值3 原值4
目标
前者正常赋值即可,但是到后者,我们发现,由于前者的更新,给后者的计算条件照成了偏差,所以我们得从右向左遍历,这样右面新更新的值就无法影响前者的更新。
滚动数组递推公式
解题代码:
public class Bag02 {
public static void main(String[] args) {
int[] weight = new int[]{1, 2, 3, 4};
int[] value = new int[]{15, 40,20, 30};
int i = maxValue(weight, value, 5);
System.out.println(i);
}
public static int maxValue(int[] weight, int[] value, int bagWeight) {
//定义一个一维数组
int[] realValue = new int[bagWeight + 1];
//初始化滚动数组
for (int i = realValue.length - 1; i >= 0; i--) {
if (i >= weight[0]){
realValue[i] = value[0];
}
}
//当重量为1时,首先给一维数组赋值
for (int i = 1; i < weight.length; i++) {
for (int j = realValue.length - 1; j >= 0; j--) {
if (weight[i] <= j) {
realValue[j] = Math.max(realValue[j], realValue[j - weight[i]] + value[i]);
} else {
continue;
}
}
}
return realValue[realValue.length - 1];
}
}