代码随想录算法训练营Day32 | 518.零钱兑换II、377. 组合总和 Ⅳ、70. 爬楼梯(进阶)

LeetCode518.零钱兑换II

518. 零钱兑换 II - 力扣(LeetCode)

1.思路

动态规划五部曲:

  1. dp[j]:凑成金额 j 的货币组合数;

  2. 在求 目标和 的那道题中,求装满背包有几种方法,这里也是求总金额凑成 amount 的方法数,所以这里的递推公式为:dp[j] = dp[j] + dp[j - coins[i]];

  3. dp[0] = 1,不放任何物品,即装满背包容量为 0 的方法是 1;

  4. 这道题里的物品和前面 01 背包不同,这里是可以无限次使用的,所以遍历时的内层背包遍历需要从前往后遍历。外层依然遍历物品。此时求得是组合数;

假设:coins[0] = 1,coins[1] = 5。

那么就是先把1加入计算,然后再把5加入计算,得到的方法数量只有{1, 5}这种情况。而不会出现{5, 1}的情况。

如果外层遍历得是背包,内层遍历物品,那么求得就是排列数了,

背包容量的每一个值,都是经过 1 和 5 的计算,包含了{1, 5} 和 {5, 1}两种情况。

  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. 组合总和 Ⅳ

377. 组合总和 Ⅳ - 力扣(LeetCode)

1.思路

动态规划五部曲:

  1. dp[i]:凑成正整数 i 得排列个数;

  2. 这道题也是求装满背包得方法,所以递推公式:dp[j] = d[j] + d[j - nums[i]];

  3. 因为递推公式 dp[i] += dp[i - nums[j]] 的缘故,dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。

  4. 本题求得是组合数,所以外层 for 循环遍历背包,内层 for 循环遍历物品;

如果把遍历 物品 放在外循环,遍历 target 作为内循环的话,举例:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有 {3,1} 这样的集合,因为 nums 遍历放在外层,3 只能出现在 1 后面;

  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.爬楼梯(进阶)

57. 爬楼梯(第八期模拟笔试)

1.思路

1阶,2阶,3阶,m阶是物品,楼顶 n 为背包。

动态规划五部曲:

  1. dp[i]:爬到楼顶为 i 个阶梯得方法数;

  2. 求装满背包有多少种方法的递推公式是:dp[j] = dp[j] + dp[j - nums[i]],这里的阶梯数从 1 ~ m,dp[i] 的来源也是这些,所以递推公式:dp[j] = dp[j] + dp[j - i];

  3. dp[0] = 1,dp[0]是递归中一切数值的基础所在,如果dp[0]是0的话,其他数值都是0了;

  4. 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.思考

还是明白最重要的一点,求组合就是先物品后背包,求排列就是先背包后物品。

4.Reference:70. 爬楼梯(进阶版) | 代码随想录

相关推荐
轻抚酸~8 小时前
KNN(K近邻算法)-python实现
python·算法·近邻算法
Yue丶越10 小时前
【C语言】字符函数和字符串函数
c语言·开发语言·算法
小白程序员成长日记11 小时前
2025.11.24 力扣每日一题
算法·leetcode·职场和发展
有一个好名字11 小时前
LeetCode跳跃游戏:思路与题解全解析
算法·leetcode·游戏
AndrewHZ12 小时前
【图像处理基石】如何在图像中提取出基本形状,比如圆形,椭圆,方形等等?
图像处理·python·算法·计算机视觉·cv·形状提取
蓝牙先生12 小时前
简易TCP C/S通信
c语言·tcp/ip·算法
稚辉君.MCA_P8_Java15 小时前
Gemini永久会员 Java中的四边形不等式优化
java·后端·算法
稚辉君.MCA_P8_Java16 小时前
通义 插入排序(Insertion Sort)
数据结构·后端·算法·架构·排序算法
无限进步_16 小时前
C语言动态内存的二维抽象:用malloc实现灵活的多维数组
c语言·开发语言·数据结构·git·算法·github·visual studio