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. Python多进程队列间传递对象

    前言 在python 需要在队列中传递对象, 会出现进程不能正常退出的情况. 其原因是因为 在父进程 向子进程传入的Queue对象不对, Queue对象正常是子进程之间的信息传递, 而当我在父进程 创 ...

  2. NumPy速查笔记(持续更新中)

    目录 1 总览 2 ndarray 3 常用API 3.1 创建ndarray (1)将Python类似数组的对象转化成Numpy数组 (2)numpy内置的数组创建 (3)从磁盘中读取标准格式或者自 ...

  3. SpringBoot---SpringMVC关于拦截器的一些问题总结

    SpringBoot---SpringMVC关于拦截器的一些问题总结 环境: IDEA :2020.1 Maven:3.5.6 SpringBoot: 2.3.2 1.直接在地址栏输入 http:// ...

  4. 精讲RestTemplate第10篇-使用代理作为跳板发送请求

    本文是精讲RestTemplate第10篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层 ...

  5. 笔记:Ubuntu安装LAMP环境

    一.更换Ubuntu的镜像源 镜像源路径:/etc/apt/sources.list 备份:cp /etc/apt/sources.list /etc/apt/souces.list.bak 使用阿里 ...

  6. 修改linux服务器的时区

    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ntpdate 1.asia.pool.ntp.org 如果出现如下错误 21 Jul 01:0 ...

  7. 你真的了解 get 和 post 的区别么

    get 和 post 是两种最常用的 HTTP 请求方法,要说它们两个的区别,相必接触过 WEB 开发的人都能够说出一二. 如果我问你这个问题,你的内心充满了自信和喜悦.你可能已经写过无数个 GET ...

  8. SQL语句组合查询 UNION

    1.使用UNION UNION 可以涉及编写多条SELECT语句,首先看看单条语句 第一条SELECT语句把Illinois,Indiana,Michigan等州的缩写传递给IN子句,检索出这些州的所 ...

  9. python 报错错误集合——更新中

    1. #!/usr/bin/env python # -*- coding:utf-8 -*- 'one #报错 File "C:\Users\shuxiu\Desktop\test.py& ...

  10. 必看!2020最新黑马JAVA 学习路线

    https://www.fang1688.cn/2020/08/24/%e5%bf%85%e7%9c%8b%ef%bc%81java-%e5%ad%a6%e4%b9%a0%e8%b7%af%e7%ba ...