题目链接:01背包
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
这篇文章主要跟大家讲解:二维数组和一维数组解决01背包问题的区别。
关于二维数组的详细图文讲解,我之前写过一篇文章,大家可以去看看二维数组解决01背包问题。
下面先给出二维数组解决“01背包”问题的模板:
for(int i = 1;i <= n;i++){ //动态规划--二维数组
for(int j = 0;j <= m;j++){
if(v[i] <= j){
f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
}
再给出一维数组解决“01背包”问题的模板:
for (int i = 0; i < M; ++i) { 动态规划--一维数组
for (int j = N; j >= costs[i]; --j) {
dp[j] = max(dp[j], dp[j - costs[i]] + values[i]);
}
}
大家对比上面的代码可以发现:
1.二维数组和一维数组都需要两层for循环分别遍历物品和背包。其中二维数组的两层for循环可以改变顺序(即先遍历背包再遍历物品),但一维数组必须先遍历物品再遍历背包。接下来详细讲一下对于二维数组,无论是先遍历背包还是先遍历物品,只是从横着修改数组的元素变成了竖着修改。当前的值依赖于二维数组左上角部分的值,所以对它没有影响。那么对于一维数组而言,如果变成先遍历背包再遍历物品,背包容量为 j 的时候,能放哪个物品已经被确定了,并且此时背包只能存放一个物品,相当于筛选了当前背包容量能存放哪一个物品的价值最大,完全不涉及动态规划,因为要依赖于它前面的值,但它前面的值全部都还没有被修改。
2.一维数组for循环遍历背包容量时,必须从大到小。这是因为我们在改变 dp[j] 时,依赖于它前面的元素,而从小到大遍历书包容量时,前面的元素已经被修改,所以会导致出错(一个物品不止一次被加入背包中)。而我们从大到小遍历书包容量就可以完美避开这个问题。那为什么又说二维数组既可以从小到大遍历又可以从大到小遍历呢?这是因为关于物品 i - 1 的所有值已经被保存下来了,不会被修改。
3.对于for循环遍历背包容量时,一维数组从大到小,并且 j >= weight[i] 。这里很容易理解,因为当 j < weight[i] 时,背包无法放入当前物品,所以一维数组的值不会被修改。只有在 j >= weight[i] 时才会修改。其实和二维数组的逻辑是一样的,只是少写了一个 if ... else ...判断语句。