518题是背包问题的变体,也称完全背包问题。

解法参考了该篇文章,然后对自己困惑的地方进行记录。

下面是该题的描述:



有一个背包,最大容量为 amount,有一系列物品 coins,每个物品的重量为 coins[i],每个物品的数量无限。请问有多少种方法,能够把背包恰好装满?求出所有的装满背包的方法数。

这里和一般的背包问题的不一样在于,物品的数量是无限的,这样的问题就称为完全背包问题。因为对于一般的0-1背包问题,物体的数量是1,要么选择该物品装入背包,要么不选择该物品装入背包。

  1. 首先思考问题的 状态选择

    状态有两个,就是「背包的容量」和「可选择的物品」,选择就是「装进背包」或者「不装进背包」 嘛,背包问题的套路都是这样。

  2. 第二步要明确 dp 数组的定义。

    首先看看刚才找到的「状态」,有两个,也就是说我们需要一个二维 dp 数组。dp[i][j] 的定义如下:

    若只使用前 i 个物品,当背包容量为 j 时,有 dp[i][j] 种方法可以装满背包。换句话说,翻译回我们题目的意思就是:

    若只使用 coins 中的前 i 个硬币的面值,若想凑出金额 j,有 dp[i][j] 种凑法。

    base case 为 dp[0][…] = 0, dp[…][0] = 1。因为如果不使用任何硬币面值,就无法凑出任何金额;如果凑出的目标金额为 0,那么“无为而治”就是唯一的一种凑法。

  3. 第三步,根据「选择」,思考状态转移的逻辑。 这一步是很重要的,做的时候就是没有准确写出状态转移条件。

    注意,我们这个问题的特殊点在于物品的数量是无限的,

    如果你不把这第 i 个物品装入背包,也就是说你不使用 coins[i] 这个面值的硬币,那么凑出面额 j 的方法数 dp[i][j] 应该等于 dp[i-1][j],继承之前的结果。

    如果你把这第 i 个物品装入了背包,也就是说你使用 coins[i] 这个面值的硬币,那么 dp[i][j] 应该等于 dp[i][j-coins[i-1]]。

    首先由于 i 是从 1 开始的,所以 coins 的索引是 i-1 时表示第 i 个硬币的面值。

    dp[i][j-coins[i-1]] 也不难理解,如果你决定使用这个面值的硬币,那么就应该关注如何凑出金额 j - coins[i-1]。

    这里的当选择第i个物体装入背包时,那么需要考虑题目给出的条件,在文章中已经强调了好几次,这里的数量是无限的,那么当说明第i个物体可以装很多次到背包中,所以当选择第i个物品时,

    dp[i][j]=dp[i-1][j]+dp[i][j-coins[i-1]],因为i是必选的,但是还可以继续选择是否装入背包

    总的方法数应该是这两者之和,(选和不选两种情况的总和)

  4. 实现代码如下:

    public int change(int amount, int[] coins) {
    
        int[][] dp = new int[coins.length + 1][amount + 1];
    for (int i = 0; i <= coins.length; i++) {
    dp[i][0] = 1;
    }
    for (int i = 1; i <= coins.length; i++) {
    for (int j = 0; j <= amount; j++) {
    if (coins[i - 1] - j > 0) {
    dp[i][j] = dp[i - 1][j];
    } else {
    dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]];
    }
    } }
    return dp[coins.length][amount];
    }
  5. 然后进行代码的优化压缩,因为dp[i][j]只依赖dp[i-1][…]和dp[i][…]

    public int change(int amount, int[] coins) {
    int[] dp = new int[amount + 1];
    dp[0] = 1;
    int n = coins.length;
    for (int i = 1; i <= n; i++) {
    for (int j = 0; j <= amount; j++) {
    if (j - coins[i-1]>=0) {
    dp[j] = dp[j] + dp[j - coins[i - 1]];
    }
    }
    }
    return dp[amount];
    }

    if (j - coins[i-1]>=0) {

    dp[j] = dp[j] + dp[j - coins[i - 1]];

    }

    因为当求dp[j]的时候,此时dp[j - coins[i - 1]已经求出来了,相当于dp[i][j-coins[i-1]],而此时的dp[i][j] 的值相当于上一轮的外层循环存储的值,即dp[i-1][j]的值,那么正好可以这样进行压缩。

  6. 时间复杂度和空间复杂度

    这样进行压缩之后,时间复杂度为O(n*amount),空间复杂度为O(amount).

LeetCode.518 零钱兑换Ⅱ(记录)的更多相关文章

  1. Java实现 LeetCode 518 零钱兑换 II

    518. 零钱兑换 II 给定不同面额的硬币和一个总金额.写出函数来计算可以凑成总金额的硬币组合数.假设每一种面额的硬币有无限个. 示例 1: 输入: amount = 5, coins = [1, ...

  2. Leetcode 518.零钱兑换II

    零钱兑换II 给定不同面额的硬币和一个总金额.写出函数来计算可以凑成总金额的硬币组合数.假设每一种面额的硬币有无限个. 注意: 你可以假设 0 <= amount (总金额) <= 500 ...

  3. 刷题-力扣-518. 零钱兑换 II

    518. 零钱兑换 II 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/coin-change-2/ 著作权归领扣网络所有.商业转载 ...

  4. LeetCode:零钱兑换【322】【DP】

    LeetCode:零钱兑换[322][DP] 题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成 ...

  5. Leetcode 322.零钱兑换

    零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: co ...

  6. Java实现 LeetCode 322 零钱兑换

    322. 零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输 ...

  7. leetcode 322零钱兑换

    You are given coins of different denominations and a total amount of money amount. Write a function ...

  8. [LeetCode]322. 零钱兑换(DP)

    题目 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: coin ...

  9. [Leetcode][动态规划] 零钱兑换

    一.题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: ...

随机推荐

  1. Go语言入门系列(六)之再探函数

    Go语言入门系列前面的文章: Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 Go语言入门系列(五)之指针和结构体的使用 在Go语言入门系列(二)之基础语法总结这篇文章中已经 ...

  2. 【趣味设计模式系列】之【代理模式2--JDK动态代理源码解析】

    1. 图解 上图主要描述了JDK动态代理的执行过程,下面做详细分析. 2. Proxy源码分析 上一篇,在使用JDK动态代理的时候,借助于Proxy类,使用newProxyInstance静态方法,创 ...

  3. 炼技术(9): 简约而不简单,永不停歇的测试 -- always_run

    最强战力,永不停歇的测试:always_run 许多工程师写完程序后,都不愿意对自己的程序做仔细测试. 很多测试说会做自动化测试,可能工作好几年都没真做过多少自动化测试. 我们的解决方案是,在系统的测 ...

  4. Pycharm中对与Python的快捷方式

    转自博客园 @python~小成录 pycharm常用快捷键与设置   pycharm高频率使用的快捷键 Ctrl+Shift+F10 运行当前的页面 Ctrl + / 注释(取消注释)选择的行 Ct ...

  5. 【Apollo】(2)--- Apollo架构设计

    Apollo架构设计 上一篇博客有讲到:[Apollo](1)--- Apollo入门介绍篇 这篇来写Apollo的核心架构设计 一.整体架构 Apollo整体架构图,已由作者宋顺已经给出: 这幅图所 ...

  6. 最新通达OA-getshell 漏洞复现

    0x00 通达简介 通达OA国内常用的办公系统,使用群体,大小公司都可以,其此次安全更新修复的高危漏洞为任意用户登录漏洞.攻击者在远程且未经授权的情况下,通过利用此漏洞,可以直接以任意用户身份登录到系 ...

  7. java十进制二进制互转

    1. 十进制转二进制 原理:给定的数循环除以2,直到商为0或者1为止.将每一步除的结果的余数记录下来,然后反过来就得到相应的二进制了. 比如8转二进制,第一次除以2等于4(余数0),第二次除以2等于2 ...

  8. golang学习笔记:Interface类型断言详情

    原文链接:https://www.2cto.com/kf/201712/703563.html 1. 用于判断变量类型 demo如下: switch t := var.(type){ case str ...

  9. AltiumDesigner画图不求人11 | 提高AD20启动速度的方法七选择手动释放工程 | 视频教程 | 你问我答

    往期文章目录 AD画图不求人1 | AD20软件安装视频教程 | 含软件安装包 AD画图不求人2 | 中英文版本切换 AD画图不求人3 | 高亮模式设置 AD画图不求人4 | 双击设计文件无法启动Al ...

  10. ubuntu 下添加环境变量

    ubuntu 下添加环境变量 方法1: 第一种临时设置,用 export 指令,如在$PATH中增加JAVA文件夹: $export PATH=$PATH:/usr/local/lib/jdk1.6. ...