为什么利用一维dp数组解决01背包问题时,需要先遍历物品,再遍历背包,且背包还是倒叙遍历呢?
这一定是很多刚刚接触01背包问题的小伙伴的疑惑的地方,下面我们就一起来解决这个问题.
首先先解释为什么需要倒叙遍历背包呢?
先解释为什么是背包容量倒序遍历 如果正序遍历,会发生什么情况?假设物品1的重量为weight[0] =1,价值为value[0]=15;
由于是正序遍历,肯定是先计算 dp[1] ,后计算 dp[2]; 注意:此时外层for循环物品遍历还只有物品0,也就是i=0;内存for循环是背包遍历
dp[1] = Math.max(dp[1],dp[1-weight[0]]+value[0]) 由于dp[1] 被初始化为0,那么 dp[1] = dp[1-weight[0]]+value[0]=dp[0]+15=15;
dp[2] = Math.max(dp[2],dp[2-weight[0]]+value[0]) 同理可以转换为dp[2] = dp[2-weight[0]]+value[0]=dp[1]+15=30;
正常情况下来讲 每个物品只能放入背包一次 ,但显然背包容量正序遍历 会出现一个物品多次放入背包,重复计算价值的情况;
反之如果是倒序遍历, 先计算 dp [2], 后计算 dp[1];
dp[2]=dp[2-weight[0]]+value[0] = dp[1]+15 =0+15 =15;
dp[1]=dp[1-weight[0]]+value[0] = dp[0]+15=0+15=15;
可以看出倒序遍历背包容量时,并没有出现重复添加某一个物品的现象;
所以背包容量需要倒序遍历!!!
再解释为什么一定要先遍历物品,后遍历背包容量
假设给定的weight[] ={2,3,1,4} 价值数组为 value[] ={10,20,15,30},最大背包容量为5;
如果先遍历背包,再遍历物品,此时dp数组到底是怎么样进行状态转移的,结合下面一幅图来看看
这是在控制台中打印的dp数组状态转移的过程; 不难发现,如果先遍历背包容量,再遍历物品,此时就相当于每次从物品中挑选一个最大价值直接作为了背包的最大价值;如第一行背包容量为5时,直接选择了重量符合条件且价值最大为30的物品4;第二行背包容量为4时,也是直接选择了重量符合条件且价值最大为30的物品4;第三行由于背包容量为3,不能装物品4,则选择了价值为20的物品2......以此类推(强行理解);
但结合二维dp数组 来理解可能会更透彻,如果是二维dp数组,那么先遍历背包,后遍历物品,实际上是一列一列的计算出每个位置的最大价值, 而对于一维数组来说, 只有一行数据, 当前行的数据是受上一行数据的影响 ,所以先遍历背包,后遍历物品本质上来说就是一个悖论;
以上仅仅只是我的个人理解,如果有错误的地方欢迎指正!!!