文章目录
- 零钱兑换
- 组合总和IV
零钱兑换
很明显,本题使用完全背包算法,求解的是组合数,直接使用完全背包算法即可,为什么是组合数呢?
如果题目说 amount=5,coins=[1,2,5] 有9种方法,那就是排列数(挑选的元素有先后顺序),如下:
5=5
5=1+2+2
5=2+1+2
5=2+2+1
5=2+1+1+1
5=1+2+1+1
5=1+1+2+1
5=1+1+1+2
5=1+1+1+1+1
class Solution {
public:
int change(int amount, vector<int>& coins) {
int m = coins.size();
int n = amount;
vector<int> dp(n + 1, 0); // dp[i]:容量为i的背包装满,最多有dp[i]种装法
dp[0] = 1;
// 组合:先物品、后容量
// 遍历物品
for(int i = 0; i < m; i++){
// 完全背包:顺序遍历容量
for(int j = coins[i]; j <= n; j++){
dp[j] = dp[j] + dp[j - coins[i]];
}
}
return dp[n];
}
};
组合总和IV
题目有强调:顺序不同的序列被视作不同的组合
即我们本题求解是排列数
dp[i]: 使用数组nums,凑成整数 i 的排列个数
dp[i] = dp[i-nums[0]] + dp[i-nums[1]] + dp[i-nums[2]] + …
举个例子比如nums = [1,2,3],target = 4
dp[4] = dp[4-1] + dp[4-2] + dp[4-3] = dp[3] + dp[2] + dp[1]
其实就是说4的排列数可以由三部分组成:1和dp[3]、2和dp[2]、3和dp[1]
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
// 完全背包
// 排列:先容量、后物品
int m = nums.size();
int n = target;
vector<int> dp(n + 1, 0);
dp[0] = 1;
for(int j = 1; j <= n; j++){
for(int i = 0; i < m; i++){
// 每一个小于当前容量j的物品nums[i],都会被当作第一个元素
if(nums[i] <= j) dp[j] += dp[j - nums[i]];
}
}
return dp[n];
}
};
为什么不能先遍历物品,后遍历容量呢?
如果先遍历物品,后遍历容量的话,举一个例子:计算dp[3]的时候,结果集只有 {1,2} 这样的集合,不会有{2,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!