动态规划-可重复装背包问题-排列数和组合数理解 2025.3.17
完全背包问题中,求组合数是外层遍历物品,内层遍历背包容量;而求排列数是外层遍历背包,内层遍历物品,这是为什么?
dp[j]值的意义:当背包容量为j的时候,有多少装满的方式(可重装)
dp递推公式:dp[j] = dp[j] (不装这个物品) + dp[j - coin.weight] (腾出这个物品的空间来装这个物品)
1.组合数:外层遍历物品,内层遍历背包容量
for (int i = 0; i < coins.size(); i++) { // 遍历物品
for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
dp[j] += dp[j - coins[i]];
//打印dp
}
}
此时由于外层遍历物品,所以物品出现的顺序一定是严格按照coins数组的下标顺序的,因此物品被加入到背包的顺序是固定的,体现了组合数
2.排列数:外层遍历背包容量,内层遍历物品
for (int j = 0; j <= amount; j++) { // 遍历背包容量
for (int i = 0; i < coins.size(); i++) { // 遍历物品
if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
}
}
此时由于内层遍历物品,所以对于每种背包的情况,物品都会被遍历一遍,所以物品数组的顺序就失去了意义,相当于被随机打乱加入背包中,所以此时是排列数
举个例子:
coins=[1,2,3,4];
amount=5;
dp=new Array(amount+1).fill(0);
dp[0]=1;
for (let j = 0; j <= amount; j++) { // 遍历背包容量
for (let i = 0; i < coins.length; i++) { // 遍历物品
if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
console.log(dp);
}
}
运行结果为:
可以看出,数据的变化是从黄圈,到蓝圈,再到绿圈。黄圈是coins(物品)从1遍历到4走了一遍的最终情况,而蓝圈是coins遍历到1的中间状态,却继承了黄圈中包含物品4的数据。这就体现了内层循环物品可以打乱物品顺序的特点
不断思考的成果: 排列数or组合数--->为了决定遍历内外层顺序--->看题意表示,包是否是“随机顺序填充”的物品: 如果是排列数思路,外层遍历物品,那么物品被放在背包里只能按照物品数组的顺序 如果是组合数思路,内层遍历物品,那么背包的每个位置都可能出现物品,相当于物品顺序打乱 如果是求“包内物品个数”,就无所谓那种遍历了