代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ

简介: 代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ

代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ

文章链接:完全背包理论基础零钱兑换 II组合总和 Ⅳ

视频链接:完全背包理论基础零钱兑换 II组合总和 Ⅳ

1. 完全背包理论基础

1.1 思路

  1. 完全背包和 01 背包的区别就是,完全背包的物品可以取无数次,也是给我们一个背包,装满之后看最大价值是多少,01 背包的物品只能取 1 次。根据背包的特性,其中的区别只体现在遍历顺序上,因此其他几步就简写了。
  2. 确定 dp 数组及其下标的含义:dp[j] 表示容量为 j 的背包,所背的物品的最大价值为 dp[j]
  3. 递推公式:dp[j]=Math.max(dp[j],dp[j-weight[i]】+value[i])
  4. dp 数组的初始化:dp[0] 表示背包容量为 0 的最大价值,就是 0 ,因为没有物品的重量为 0 可以放入背包中;非 0 下标,看递推公式 dp[j]=Math.max(dp[j],dp[j-weight[i]]+value[i])也是初始化时就应该把它初始化为非负数里的最小值即可,也就是 0
  5. 遍历顺序:因为我们之前的两层 for循环是先物品后背包,背包这一层是倒序遍历,为的就是让物品只使用一次,而我们现在可以无限使用,因此背包这一层就改为正序遍历。即 for(int i=0;i<weight.length();i++)for(int j=weight[i];j<=bagWeight;j++)bagWeight 就是背包的容量。
  6. 其实这里遍历顺序是先物品后背包,这里是可以颠倒的,可以先背包再物品。颠倒后是先确定了背包的容量再挨个放物品,从列上往下加物品,而先物品再背包则是从行上往右加物品,而我们的递推公式的第 i 个物品的 dp[j] 依赖的都是左边的 dp[j-1]dp[j-2] 这些值,而无论是从上往下还是从左往右,左边的 dp[j-1]dp[j-2] 永远有值,因此颠倒顺序是可以的。01 背包不可以是因为第 i 个物品的 dp[j] 依赖的是第 i-1 个物品的 dp 数组


  7. 打印 dp 数组:用于 debug

1.2 代码

//先遍历物品,再遍历背包
private static void testCompletePack(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 0; i < weight.length; i++){ // 遍历物品
        for (int j = weight[i]; j <= bagWeight; j++){ // 遍历背包容量
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}
//先遍历背包,再遍历物品
private static void testCompletePackAnotherWay(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
        for (int j = 0; j < weight.length; j++){ // 遍历物品
            if (i - weight[j] >= 0){
                dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
            }
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}

2. LeetCode 518. 零钱兑换 II

2.1 思路

  1. 本题和纯完全背包不同,纯完全背包求的是凑成背包的最大价值,本题求的是凑成总金额的组合数,注意是组合数,即 [2,1,2] 和 [2,2,1] 是一样的。这题和494. 目标和有点类似都是给一些物品和一个集合,问装满这个集合有多少种方法,和本题的区别是那题的物品只能用一次,本题的物品可以用无数次,因此那题是 01 背包,本题是完全背包
  2. dp 数组及其下标的含义:装满背包容量为 j 的背包有 dp[j] 种方法。最终求的是 dp[amount]
  3. 递推公式:就是我们放 i 个物品所对应的有多少种方法进行累加,即 dp[j]+=dp[j-coins[i]]。
  4. dp 数组的初始化:dp[0]=1,因为递推公式的通过前一个累加的,因此第一个不能为 0,为 0 就后面全是 0 了。dp[0] 的含义就是装满背包容量为 0 的方法有 1 种,空集也是一个集合。非 0 下标就是都是 0 即可,因为我们是累加的,为 0 就不会影响递推公式
  5. 遍历顺序,在上面纯完全背包上说了物品和背包的遍历顺序是可以颠倒的,但在本题就有点要求了,因为纯完全背包求的是装满后的最大价值,本题求的是装满后的最大组合数,而且还是组合,因此对遍历顺序也就有要求了。我们是先物品再背包 for(int i=0;i<coins.length;i++)for(int j=coins[i];j<=amonut;j++),dp[j]+=dp[j-coins[i]]背包这一层是正序遍历,因为物品可以使用无数次。而如果先背包后物品,就是物品在后,就会遍历多轮,从而出现重复组合的情况,如果物品在前,物品只遍历了一轮,因此本题不能颠倒顺序,求组合数就先物品再背包,求排列数就是先背包再物品。
  6. 打印 dp 数组:用于 debug

2.2 代码

class Solution {
    public int change(int amount, int[] coins) {
        //递推表达式
        int[] dp = new int[amount + 1];
        //初始化dp数组,表示金额为0时只有一种情况,也就是什么都不装
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
}

3. LeetCode 377. 组合总和 Ⅳ

3.1 思路

  1. 本题问用数组里的数凑成 target 有多少种方法,数组里的数可以使用无限次,因此是完全背包。本题求出的集合是讲究顺序的,也就是排列而不是组合,这题和518. 零钱兑换 II很相似,只是本题是求排列的,因此本题的动规五步曲其实就遍历顺序不一样,其他都是一样的,下面就简写了。
  2. dp 数组及其下标的含义:装满背包容量为 j 的背包有 dp[j] 种方法。最终求的是 dp[target]
  3. 递推公式:就是我们放 j 个物品所对应的有多少种方法进行累加,即 dp[i]+=dp[i-nums[j]]。
  4. dp 数组的初始化:dp[0]=1。非 0 下标就是都是 0 即可。
  5. 遍历顺序:在518. 零钱兑换 II说过了先物品再背包得到的是组合数,先背包再物品得到的是排列数,因为先背包再物品,物品在第二层循环里是每次都从开始往后取的,因此每次都能遍历多轮,因此得到的是排列数。举个例子:先物品再背后时计算 dp[4] 时只会出现 [1,3] 而不会出现 [3,1],因为是先物品再背包,3 是在 1 后面的,因此不会在 3 后面再出现 1 了。
  6. 打印 dp 数组:用于 debug
  7. 总结一下完全背包问题:对于纯完全背包,即求装满后的最大价值是什么,或者问能不能装满这个背包,那么两层 for 循环颠倒是可以的。但对完全背包问装满这个背包有多少种方法时就要区分是求的是组合数还是排列数,如果是组合数就是先物品再背包,如果是排列数就是先背包再物品

3.2 代码

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for (int i = 0; i <= target; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (i >= nums[j]) {
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
}
相关文章
|
4天前
|
机器学习/深度学习 存储 算法
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
近端策略优化(PPO)是深度强化学习中高效的策略优化方法,广泛应用于大语言模型的RLHF训练。PPO通过引入策略更新约束机制,平衡了更新幅度,提升了训练稳定性。其核心思想是在优势演员-评论家方法的基础上,采用裁剪和非裁剪项组成的替代目标函数,限制策略比率在[1-ϵ, 1+ϵ]区间内,防止过大的策略更新。本文详细探讨了PPO的基本原理、损失函数设计及PyTorch实现流程,提供了完整的代码示例。
104 10
近端策略优化(PPO)算法的理论基础与PyTorch代码详解
|
2月前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
99 1
|
3月前
|
存储 缓存 算法
通过优化算法和代码结构来提升易语言程序的执行效率
通过优化算法和代码结构来提升易语言程序的执行效率
|
3月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
3月前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
67 3
|
3月前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
2天前
|
算法 数据安全/隐私保护 计算机视觉
基于FPGA的图像双线性插值算法verilog实现,包括tb测试文件和MATLAB辅助验证
本项目展示了256×256图像通过双线性插值放大至512×512的效果,无水印展示。使用Matlab 2022a和Vivado 2019.2开发,提供完整代码及详细中文注释、操作视频。核心程序实现图像缩放,并在Matlab中验证效果。双线性插值算法通过FPGA高效实现图像缩放,确保质量。
|
1月前
|
算法 数据安全/隐私保护 计算机视觉
基于Retinex算法的图像去雾matlab仿真
本项目展示了基于Retinex算法的图像去雾技术。完整程序运行效果无水印,使用Matlab2022a开发。核心代码包含详细中文注释和操作步骤视频。Retinex理论由Edwin Land提出,旨在分离图像的光照和反射分量,增强图像对比度、颜色和细节,尤其在雾天条件下表现优异,有效解决图像去雾问题。
|
1月前
|
算法 数据可视化 安全
基于DWA优化算法的机器人路径规划matlab仿真
本项目基于DWA优化算法实现机器人路径规划的MATLAB仿真,适用于动态环境下的自主导航。使用MATLAB2022A版本运行,展示路径规划和预测结果。核心代码通过散点图和轨迹图可视化路径点及预测路径。DWA算法通过定义速度空间、采样候选动作并评估其优劣(目标方向性、障碍物距离、速度一致性),实时调整机器人运动参数,确保安全避障并接近目标。
147 68