背包问题模板
前言
一般纯完全背包问题(每件物品都有无限个,也就是可以放入背包多次)求得装满背包的最大价值是多少,和凑成总和的元素没有顺序没关系,所以两个for嵌套循环可以互换顺序:
- for所有物品
- for背包的尺寸(直到最大重量)
Template
// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
// 先遍历背包 再遍历物品
for(int i = weight[i]; i <= bagWeight; i++) { // 遍历背包容量
for(int j = 0; j < weight.size() ; j++) { // 遍历背包容量
if(i - weight[j] >= 0)
dp[i] = max(dp[i], dp[i - weight[j]] + value[j]);
}
}
组合与排列
递推公式
凑成总金额j的货币组合数为dp[j],j 为5,
已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来
所以求组合类问题的公式,都是类似这种:
dp[j] += dp[j - nums[i]]
组合题目
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
示例 1:
输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
组合模板
组合不强调元素之间的顺序
dp[0] = 1;
for (int i = 0; i < coins.size(); i++) { // 遍历物品
for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
dp[j] += dp[j - coins[i]];
}
}
输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:
-
i = 0时
dp[1] = dp[1] + dp[1-1] = 1;
dp[2] = dp[2] + dp[2-1] = 1;
dp[3] = 1;
dp[4] = 1;
dp[5] = 1; -
i = 1时
dp[2] = dp[2] + dp[2-2] = 1 + 1 =2;
dp[3] = dp[3] + dp[3-2] = 1 + 1 = 2;
dp[4] = 3;
dp[5] = 3; -
i = 2时
dp[5] = dp[5] + dp[5-5] = 3 + 1 = 4;
所以cout为dp[amount] = dp[5] = 4
对于最外层为物品,那么该物品对应的coins,只会从0,1,2开始遍历,且只一次,即出现{1,2,2},不可能出现{2,2,1}。
排列题目
给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
示例:
coins = [1, 2, 3]
amount = 4
所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1)
请注意,顺序不同的序列被视作不同的组合。
因此输出为 7。
排列模板
排列强调元素之间的顺序
dp[0] = 1;
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],amount = 4,dp[i]值为:
-
dp[0] = 1;
-
j = 1, i = 0时,dp[1] = dp[0] = 1; 后面循环不成立
-
j = 2
i = 0时,dp[2] = dp[2] + dp[1] = 1;
i = 1时,dp[2] = dp[2] + dp[0] = 1 + 1 = 2;后面循环不成立 -
j = 3
i = 0时,dp[3] =
~
dp[3] = dp[2] + dp[1] + dp[0] = 4 -
j = 4
i = 0时
~
dp[amount] = dp[4] = dp[3] + dp[2] + dp[1] = 7;
对于{1,3}和{3,1},由于最外层为背包容量,内层为物品,则背包容量变化时,都是从第一个物品开始遍历