【动态规划】02-关于内外层循环先遍历背包还是物品的问题
文中提到了
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举一个例子:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!
我们说的都是一维dp的内外层先遍历哪个的问题,基于此讨论。
nums = [1, 2, 3]
target = 4
所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1)
我们先来看常见的情况:
- 外层for遍历物品,内层for循环遍历背包 (组合)
dp[j - nums[i]] 如果外层i表示物品,内层j表示背包。
物品就是从0-i。物品出现的顺序就只能是0-i的顺序。就是最外层循环i的时候,循环到1 就只有0,1这些物体,循环到2,这个时候是在0,1的基础上,再加2物体。也就是说,对于每次滚动跟新的过程,物品是随着更新的过程,慢慢被加进来的,每次都可见的是所有的背包容量(从0-j)。- 在某一次滚动更新过程,它是固定i(物品种类),改变j(背包容量)
它根据变化的j(背包容量),依次选择我当前放物品0,放物品1,...,放物品i,能到背包容量j的情况。
只能选择已经遍历到的物品,比如i=1, 那就只能在物品0,物品1中选择,然后放入背包重量为0,背包重量为1,...,背包重量为j的情况。
- 在某一次滚动更新过程,它是固定i(物品种类),改变j(背包容量)
再来看:
- 外层for,i遍历背包,内层for,j循环遍历物品 (排列)
dp[i - nums[j] 外层i表示背包,内层j表示物品。
因为内层物品每次都会从0-j,所以在滚动更新的过程中,对于i=4,也就是背包重量确定为某一个值的时候,它会开始看有哪些物品可以满足背包的条件(此时要求背包能背的重量为1),它在一次更新过程是能看到所有的物品的。这也是为啥说它与排列更像,因为排列本身就是要满足条件的物品中的所有物品参与进来。- 在某一次滚动更新过程,它是固定i(背包容量),改变j(物品种类), 也就是
dp[4] = dp[4-nums[0]] + dp[4-nums[1]] + dp[4-nums[2]], 也就是
dp[4] = dp[3] + dp[2] + dp[1]
这里的dp[3],就是我准备放nums[0]了,那你要给我腾出nums[0]的位置,那就是dp[3], 在这个基础上,再加一个nums[0] (1) 就得到target 4了
这里仔细感受一下,是不是就是它根据变化的j(物品种类),依次选择我当前放物品0,放物品1,...,放物品j,能到背包容量i的情况。
![image]()
- 在某一次滚动更新过程,它是固定i(背包容量),改变j(物品种类), 也就是
class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for (int i = 0; i <= target; i++) { // 外层i循环遍历背包
for (int j = 0; j < nums.length; j++) { // 内层j循环遍历物品
if (i >= nums[j]) {
dp[i] += dp[i - nums[j]]; // 依次选择当前放物品j
}
}
}
return dp[target];
}
}
总结
-
对于外层for循环遍历物品,内层for遍历背包的情况
在一次滚动更新的过程中(此时滚动的是物品的种类),它考虑了全部的背包重量,适用于组合这种,元素位置不同,算同一情况的,也就是不管元素位置怎么样,只统计一遍,那我按照物品遍历的顺序,只是其中的一种,但是也是组合了。 -
对于外层for遍历背包,内层for循环遍历物品的情况
在一次滚动更新的过程中(此时滚动的是背包的容量),它考虑了全部的物品种类,适用于排列这种,元素位置不同,算不同情况的。
本文来自博客园,作者:chendsome,转载请注明原文链接:https://www.cnblogs.com/chendsome/p/18558773


浙公网安备 33010602011771号