LeetCode 322. 零钱兑换
322. 零钱兑换
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
输入:coins = [2], amount = 3
输出:-1
输入:coins = [1], amount = 0
输出:0
题解
看到这道题的时候,有两个想法,背包问题,深搜。因为硬币就是物品,总额就是背包容量。当我没放入一种硬币的时候,减去相应的价值,然后深搜递归继续搜索剩下价值的硬币数量。但是这种办法会超时,因为越是越小价值的总额,调用的次数越频繁,有没有更好的办法解决这个问题呢,那就是记忆搜索法。
记忆搜索法
第一次接触记忆搜索法的时候好像是斐波拉契序列,因为f(n) = f(n-1) + f(n-2),像f(0),f(1)这些会频繁递归调用,这样的效率非常低,所以就有人想出了打表方法,就是把所有可能的斐波拉契序列先求出来,把f(0)换成a[0],f(1)换成a[1],这样调用函数变成去数组元素。
使用背包+深搜的时候,最大的问题就是递归调用过多,我们可以利用空间换时间,把调用函数换成数组。
class Solution {public int coinChange(int[] coins, int amount) {if(amount < 1){//总额小于1,直接返回0return 0;}return coinChange(coins, amount, new int[amount]);}int coinChange(int[] coins, int amount, int[] count){if(amount < 0){//总额小于0,返回-1,表示不能放入这个硬币return -1;}if(amount == 0){//总额为0,表示放入这个硬币之后刚刚好,无须在放入银币return 0;}if(count[amount - 1] != 0){//把递归调用换为数组return count[amount - 1];}int min = Integer.MAX_VALUE;for(int coin : coins){//遍历每个硬币int res = coinChange(coins, amount - coin, count);//递归调用搜索结果集if(res >= 0 && res < min){//返回结果大于等于0,结果需要小于其他枚举的硬币数量侧,才更新最小值min = 1 + res;}}count[amount - 1] = (min == Integer.MAX_VALUE) ? -1 : min;return count[amount - 1];}
}
这个方法时间效率也不是很好,因为给出的数据硬币是升序的,每次枚举都是从小到大进行枚举,我们其实也可以加入贪心算法,对硬币进行降序排序,然后通过剪枝等来降低时间复杂度。
动态规划
其实背包问题就是动态规划,但是记忆搜索法是通过搜索状态来完成状态的转移,这样时间效率上和暴搜没什么区别,我们需要找到动态转移方程才行。
背包问题的动态转移方程是没当遍历到一个物品的时候,看看能不能放进去这个物品,然后在背包里面腾出一个容量为这个物品的地方,再判断当前价值是否增加。(dp[i] = max( dp[i], dp[i - w[j]]))
硬币问题的动态转移方程应该是dp[i] = min( dp[i], dp[i - c[j]]),当放入第i个硬币的时候,看看会不会使硬币数量变小,变小的话就更新状态。
所以状态转移方程为:
- 当 i = 0时,dp[ i ] = 0
- 当 i > 0时,dp[ i ] = min(dp[ i - c[ j ] ], dp[ i ])
class Solution {public int coinChange(int[] coins, int amount) {int max = amount + 1;int[] dp = new int[amount + 1];//动态数组Arrays.fill(dp, max);//用最大值填充数组,硬币数量不会超过数组容量 + 1dp[0] = 0;//初始化for(int i = 1; i <= amount; i++){//遍历状态for(int j = 0; j < coins.length; j++){//遍历硬币if(coins[j] <= i){//总额大于硬币面值dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);//动态转移方程}}}return dp[amount] > amount ? -1 : dp[amount];}
}
LeetCode 322. 零钱兑换相关推荐
- leetcode: 322.零钱兑换
322.零钱兑换 来源:力扣(LeetCode) 链接: https://leetcode.cn/problems/coin-change/ 给你一个整数数组 coins ,表示不同面额的硬币:以及一 ...
- LeetCode 322. 零钱兑换(DP)
文章目录 1. 题目信息 2. 解题 2.1 回溯穷举 2.2 动态规划 1. 题目信息 给定不同面额的硬币 coins 和一个总金额 amount. 编写一个函数来计算可以凑成总金额所需的最少的硬币 ...
- golang力扣leetcode 322.零钱兑换
322.零钱兑换 322.零钱兑换 题解 代码 322.零钱兑换 322.零钱兑换 题解 //state: dp[i]金额为i时所需最少硬币个数 //function: dp[i]=dp[i-n]+1 ...
- Java实现 LeetCode 322 零钱兑换
322. 零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输 ...
- [LeetCode] 322.零钱兑换 五种方法讲解
322.零钱兑换 五种方法讲解 文章目录 322.零钱兑换 五种方法讲解 1 问题描述 2 问题分析 3 解决策略 3.1 递归-暴力解决 3.2 递归-加入存储 3.3 BFS 3.4 动态规划-自 ...
- Leetcode.322 零钱兑换
索引iii表示 amountamountamount 金额,dp[i]dp[i]dp[i] 表示最少 coinscoinscoins 个数. 递归 class Solution {int res = ...
- Leetcode 322.零钱兑换
Time: 20190906 Type: Medium 题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币 ...
- leetcode 322. 零钱兑换 思考分析
目录 1.题目 2.思路分析 3.参考链接 1.题目 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总 ...
- 99. Leetcode 322. 零钱兑换 (动态规划-完全背包)
步骤一.确定状态: 确定dp数组及下标含义 dp数组是个amout+1的数组,dp[j]表示的是装满容量为j的背包所需要的最 少物品的个数 步骤二.推断状态方程: 对于当前物品i, 有两种选择决定了d ...
最新文章
- java中velocity定义宏标签_velocity自定义标签和指令(转:zwj)
- oracle双机python连接_Python连接Oracle
- C语言之基本算法26—佩尔方程求解
- Python3字符串复制
- 软件测试用例优秀例子_功能测试用例设计方法分享
- 当互联网人有了孩子后...
- Android常用开源框架
- 支付宝当前最新版集成2.1.2版本
- iPhone 9又要鸽了?
- 快照隔离(Snapshot Isolation)简单介绍和例子
- python操作注册表能干啥_转 python操作注册表模块_winreg
- oracle中使用kill,WINDOWS和LINUX服务器级别的KILLORACLE进行方法
- 【转】NB的specify
- 英威腾GD200A系列变频器实现多段速控制的相关参数设置及接线
- socket.io实现一对多的在线咨询客服系统
- Cortana搜索框怎么在任务栏显示?
- iOS苹果个人开发者账号购买流程 2018 版
- 【数组练习题】计算一下牧场中的草丛数量(详细代码)
- 推荐一款超快的Android模拟器
- 在Windows7和Ubuntu上编译安装MICO