LeetCode518.零钱兑换II
1.思路
动态规划五部曲:
-
dp[j]:凑成金额 j 的货币组合数;
-
在求 目标和 的那道题中,求装满背包有几种方法,这里也是求总金额凑成 amount 的方法数,所以这里的递推公式为:dp[j] = dp[j] + dp[j - coins[i]];
-
dp[0] = 1,不放任何物品,即装满背包容量为 0 的方法是 1;
-
这道题里的物品和前面 01 背包不同,这里是可以无限次使用的,所以遍历时的内层背包遍历需要从前往后遍历。外层依然遍历物品。此时求得是组合数;
假设:coins[0] = 1,coins[1] = 5。
那么就是先把1加入计算,然后再把5加入计算,得到的方法数量只有{1, 5}这种情况。而不会出现{5, 1}的情况。
如果外层遍历得是背包,内层遍历物品,那么求得就是排列数了,
背包容量的每一个值,都是经过 1 和 5 的计算,包含了{1, 5} 和 {5, 1}两种情况。
- amount = 5,coins = [1,2,5],dp:

cpp
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int>dp(amount+1,0);
dp[0]=1;
for(int i=0;i<coins.size();i++){
for(int j=coins[i];j<=amount;j++){
if(dp[j]<INT_MAX-dp[j-coins[i]]){
dp[j]=dp[j]+dp[j-coins[i]];
}
}
}
return dp[amount];
}
};
2.复杂度分析
时间复杂度:O(m*n) - m 是 amount,n 是 coins 得长度
空间复杂度:O(m)
3.思考
这道题代码一写出来就是很简单,不过却很难思考。
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
如果物品只能用一次,内层 for 就反向遍历。
如果物品能重复使用,内层 for 就正向遍历。
4.Reference:518.零钱兑换II | 代码随想录
LeetCode377. 组合总和 Ⅳ
1.思路
动态规划五部曲:
-
dp[i]:凑成正整数 i 得排列个数;
-
这道题也是求装满背包得方法,所以递推公式:dp[j] = d[j] + d[j - nums[i]];
-
因为递推公式 dp[i] += dp[i - nums[j]] 的缘故,dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。
-
本题求得是组合数,所以外层 for 循环遍历背包,内层 for 循环遍历物品;
如果把遍历 物品 放在外循环,遍历 target 作为内循环的话,举例:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有 {3,1} 这样的集合,因为 nums 遍历放在外层,3 只能出现在 1 后面;
- nums = [1,2,3],target = 4

cpp
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int>dp(target+1,0);
dp[0]=1;
for(int j=0;j<=target;j++){
for(int i=0;i<nums.size();i++){
if(j>=nums[i] && dp[j]<=INT_MAX-dp[j-nums[i]]) dp[j]=dp[j]+dp[j-nums[i]];
}
}
return dp[target];
}
};
注:C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num] 。
2.复杂度分析
时间复杂度:O(target*n)
空间复杂度:O(target)
3.Reference:377. 组合总和 Ⅳ | 代码随想录
LeetCode70.爬楼梯(进阶)
1.思路
1阶,2阶,3阶,m阶是物品,楼顶 n 为背包。
动态规划五部曲:
-
dp[i]:爬到楼顶为 i 个阶梯得方法数;
-
求装满背包有多少种方法的递推公式是:dp[j] = dp[j] + dp[j - nums[i]],这里的阶梯数从 1 ~ m,dp[i] 的来源也是这些,所以递推公式:dp[j] = dp[j] + dp[j - i];
-
dp[0] = 1,dp[0]是递归中一切数值的基础所在,如果dp[0]是0的话,其他数值都是0了;
-
1、2 步 和 2、1 步都是上三个台阶,但是这两种方法不一样,所以这里求的是排列数,所以外层循环背包,内层循环物品;
cpp
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n,m;
while(cin>>n>>m){
vector<int>dp(n+1,0);
dp[0]=1;
for(int j=1;j<=n;j++){
for(int i=1;i<=m;i++){
if(j>=i){
dp[j]=dp[j]+dp[j-i];
}
}
}
cout<<dp[n];
}
return 0;
}
2.复杂度分析
时间复杂度:O(n*m)
空间复杂度:O(n)
3.思考
还是明白最重要的一点,求组合就是先物品后背包,求排列就是先背包后物品。