LeetCode.518 零钱兑换Ⅱ(记录)
518题是背包问题的变体,也称完全背包问题。
解法参考了该篇文章,然后对自己困惑的地方进行记录。
下面是该题的描述:

有一个背包,最大容量为 amount,有一系列物品 coins,每个物品的重量为 coins[i],每个物品的数量无限。请问有多少种方法,能够把背包恰好装满?求出所有的装满背包的方法数。
这里和一般的背包问题的不一样在于,物品的数量是无限的,这样的问题就称为完全背包问题。因为对于一般的0-1背包问题,物体的数量是1,要么选择该物品装入背包,要么不选择该物品装入背包。
首先思考问题的 状态和选择
状态有两个,就是「背包的容量」和「可选择的物品」,选择就是「装进背包」或者「不装进背包」 嘛,背包问题的套路都是这样。第二步要明确 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,那么“无为而治”就是唯一的一种凑法。
第三步,根据「选择」,思考状态转移的逻辑。 这一步是很重要的,做的时候就是没有准确写出状态转移条件。
注意,我们这个问题的特殊点在于物品的数量是无限的,
如果你不把这第 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是必选的,但是还可以继续选择是否装入背包
总的方法数应该是这两者之和,(选和不选两种情况的总和)
实现代码如下:
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];
}
然后进行代码的优化压缩,因为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]的值,那么正好可以这样进行压缩。
时间复杂度和空间复杂度
这样进行压缩之后,时间复杂度为O(n*amount),空间复杂度为O(amount).
LeetCode.518 零钱兑换Ⅱ(记录)的更多相关文章
- Java实现 LeetCode 518 零钱兑换 II
518. 零钱兑换 II 给定不同面额的硬币和一个总金额.写出函数来计算可以凑成总金额的硬币组合数.假设每一种面额的硬币有无限个. 示例 1: 输入: amount = 5, coins = [1, ...
- Leetcode 518.零钱兑换II
零钱兑换II 给定不同面额的硬币和一个总金额.写出函数来计算可以凑成总金额的硬币组合数.假设每一种面额的硬币有无限个. 注意: 你可以假设 0 <= amount (总金额) <= 500 ...
- 刷题-力扣-518. 零钱兑换 II
518. 零钱兑换 II 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/coin-change-2/ 著作权归领扣网络所有.商业转载 ...
- LeetCode:零钱兑换【322】【DP】
LeetCode:零钱兑换[322][DP] 题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成 ...
- Leetcode 322.零钱兑换
零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: co ...
- Java实现 LeetCode 322 零钱兑换
322. 零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输 ...
- leetcode 322零钱兑换
You are given coins of different denominations and a total amount of money amount. Write a function ...
- [LeetCode]322. 零钱兑换(DP)
题目 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: coin ...
- [Leetcode][动态规划] 零钱兑换
一.题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输入: ...
随机推荐
- Python多进程队列间传递对象
前言 在python 需要在队列中传递对象, 会出现进程不能正常退出的情况. 其原因是因为 在父进程 向子进程传入的Queue对象不对, Queue对象正常是子进程之间的信息传递, 而当我在父进程 创 ...
- NumPy速查笔记(持续更新中)
目录 1 总览 2 ndarray 3 常用API 3.1 创建ndarray (1)将Python类似数组的对象转化成Numpy数组 (2)numpy内置的数组创建 (3)从磁盘中读取标准格式或者自 ...
- SpringBoot---SpringMVC关于拦截器的一些问题总结
SpringBoot---SpringMVC关于拦截器的一些问题总结 环境: IDEA :2020.1 Maven:3.5.6 SpringBoot: 2.3.2 1.直接在地址栏输入 http:// ...
- 精讲RestTemplate第10篇-使用代理作为跳板发送请求
本文是精讲RestTemplate第10篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层 ...
- 笔记:Ubuntu安装LAMP环境
一.更换Ubuntu的镜像源 镜像源路径:/etc/apt/sources.list 备份:cp /etc/apt/sources.list /etc/apt/souces.list.bak 使用阿里 ...
- 修改linux服务器的时区
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ntpdate 1.asia.pool.ntp.org 如果出现如下错误 21 Jul 01:0 ...
- 你真的了解 get 和 post 的区别么
get 和 post 是两种最常用的 HTTP 请求方法,要说它们两个的区别,相必接触过 WEB 开发的人都能够说出一二. 如果我问你这个问题,你的内心充满了自信和喜悦.你可能已经写过无数个 GET ...
- SQL语句组合查询 UNION
1.使用UNION UNION 可以涉及编写多条SELECT语句,首先看看单条语句 第一条SELECT语句把Illinois,Indiana,Michigan等州的缩写传递给IN子句,检索出这些州的所 ...
- python 报错错误集合——更新中
1. #!/usr/bin/env python # -*- coding:utf-8 -*- 'one #报错 File "C:\Users\shuxiu\Desktop\test.py& ...
- 必看!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 ...