目录

  • 1、题目
  • 2、思路分析
  • 3、参考链接

1、题目

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

你可以认为每种硬币的数量是无限的。

提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

2、思路分析

这一题和leetcode 39. 组合总和 思考分析有点像,不过要求不同。
39题要求的是所有解的集合,而这一题求的是最优解。
所以直接套用39回溯思路,然后从子解中找到最小的即可,貌似也是能做的,不过会超时。。。

class Solution {public:int left_sum;int min_coin_nums;int coin_nums;void backtracking(vector<int>& coins,int startindex){   if(left_sum < 0) return;if(left_sum == 0){if(min_coin_nums==0) min_coin_nums=coin_nums;else min_coin_nums=min(min_coin_nums,coin_nums);return;}for(int i=startindex;i<coins.size();i++){if(left_sum-coins[i]<0) break;//处理结点;coin_nums++;left_sum-=coins[i];//递归,探索下一层backtracking(coins,i);      //递归//回溯,撤销处理结果left_sum+=coins[i];coin_nums--;}}int coinChange(vector<int>& coins, int amount) {min_coin_nums=0;coin_nums=0;left_sum=amount;//排序加速剪枝sort(coins.begin(),coins.end());backtracking(coins,0);if((min_coin_nums ==0 && amount == 0)||(min_coin_nums != 0)) return min_coin_nums;return -1;}
};

其实这一题是一道动态规划题:
如果想求amount = 11时的最少硬币数(原问题),如果知道amout =10的最少硬币数(子问题),你只需要把子问题的答案加一(再选一枚1元的硬币),因为硬币的数量是没有限制的,当然也可能是amout =6的最少硬币数加上一个面额为5的硬币。这时候就需要取最少的硬币数了。
子问题之间是相互独立的。
1、分析最优子结构:
凑成面值为 11 的最少硬币个数可以由以下三者的最小值得到:

凑成面值为 10 的最少硬币个数 + 面值为 1 的这一枚硬币;
凑成面值为 9 的最少硬币个数 + 面值为 2 的这一枚硬币;
凑成面值为 6 的最少硬币个数 + 面值为 5 的这一枚硬币。
dp[11] = min (dp[10] + 1, dp[9] + 1, dp[6] + 1)

2、确定【DP状态】
dp[i] :凑齐总价值 i 需要的最少硬币个数;
3、确定状态转移方程

for(int i = 0;i < coins.size();i++)
{if (coin[i] <= amount)dp[amount] = min(1 + dp[amount - coin[i]])  }


需要注意的地方:
单枚硬币的面值首先要小于等于 当前要凑出来的面值。

class Solution {public:int coinChange(vector<int>& coins, int amount) {int n = coins.size();//对dp数组中每个值先赋一个不可能的值,因为要比较的是最小值,这个不可能的值就得赋值成为一个最大值,这里只需要比总金额大就行了,表示凑不出来//dp[i] :凑齐总价值 i 需要的最少硬币个数;vector<int> dp(amount + 1,amount+1);//凑总金额为0,不需要硬币dp[0] = 0;//从0到amount,计算凑齐总价值 i 需要的最少硬币个数;for(int i = 0;i <=amount;i++){//从0到coins.size(),遍历每个面额的硬币for(int j = 0;j < n;j++){//总价值必须比该硬币面额大//并且dp[i - coins[j]]必须被赋过值,也就是说必须有个方案,要能够凑出来,这样才能进行下一步推导if(i - coins[j] >= 0 && dp[i - coins[j]] != amount+1){//如果满足这个条件,那么dp[i]就是dp[i]和当前方案中的最小值,因为dp[i]可能被多次赋值,我们取的是最优值dp[i] = min(dp[i],1 + dp[i - coins[j]]);}}}//dp[amount]没有没赋值,说明没有组合方案,所以应该返回-1if(dp[amount] == amount+1){return -1;}return dp[amount];}};

时间复杂度:O(N \times amount)O(N×amount),这里 NN 是可选硬币的种类数,amountamount 是题目输入的面值;
空间复杂度:O(amount)O(amount),状态数组的大小为 amountamount。
由于dp数组是自底向上求解的,所以过程中不会出现重叠子问题
需要注意的地方:

1、数组初始化把初值amount+1换成Integer.MAX_VALUE为什么就不行了 ?
如果初值赋值为正无穷,dp[i - coin] +1 可能会发生整型溢出。
2、循环的判断条件if (i - coin >= 0 && dp[i - coin] != amount + 1)为什么要判断 dp[i - coin] != amount + 1呢
amount + 1 在这里表示的是当前状态表示的金额不能被候选硬币的和表示。
去掉dp[i-coin] != amount + 1 这个判断条件也是可以的, 因为若是 dp[i-coin] = amount + 1, 在下一步 dp[i] = Math.min(dp[i], 1 + dp[i - coin]) 也会将amount+1+1这个值过滤掉的,即amount + 1仍然是无效的。 因为数组中的有效值不会超过amount+1,就算有1块钱的硬币,最大值也就是amount,因此在取两个数的最小值时已经自动将amount+1这个值过滤掉了。

3、参考链接

动态规划、完全背包、BFS(包含完全背包问题公式推导)
labuladong的公众号文章

leetcode 322. 零钱兑换 思考分析相关推荐

  1. [LeetCode] 322.零钱兑换 五种方法讲解

    322.零钱兑换 五种方法讲解 文章目录 322.零钱兑换 五种方法讲解 1 问题描述 2 问题分析 3 解决策略 3.1 递归-暴力解决 3.2 递归-加入存储 3.3 BFS 3.4 动态规划-自 ...

  2. leetcode: 322.零钱兑换

    322.零钱兑换 来源:力扣(LeetCode) 链接: https://leetcode.cn/problems/coin-change/ 给你一个整数数组 coins ,表示不同面额的硬币:以及一 ...

  3. LeetCode 322. 零钱兑换(DP)

    文章目录 1. 题目信息 2. 解题 2.1 回溯穷举 2.2 动态规划 1. 题目信息 给定不同面额的硬币 coins 和一个总金额 amount. 编写一个函数来计算可以凑成总金额所需的最少的硬币 ...

  4. golang力扣leetcode 322.零钱兑换

    322.零钱兑换 322.零钱兑换 题解 代码 322.零钱兑换 322.零钱兑换 题解 //state: dp[i]金额为i时所需最少硬币个数 //function: dp[i]=dp[i-n]+1 ...

  5. Java实现 LeetCode 322 零钱兑换

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

  6. LeetCode 322. 零钱兑换

    322. 零钱兑换 难度 中等 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amount ,表示总金额. 计算并返回可以凑成总金额所需的 最少的硬币个数 .如果没有任何一种硬币组 ...

  7. Leetcode.322 零钱兑换

    索引iii表示 amountamountamount 金额,dp[i]dp[i]dp[i] 表示最少 coinscoinscoins 个数. 递归 class Solution {int res = ...

  8. Leetcode 322.零钱兑换

    Time: 20190906 Type: Medium 题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币 ...

  9. [leetcode]322. 零钱兑换(Coin Change )C++代码实现

    1,题目描述 2,题目分析 3,代码实现 class Solution { public:int coinChange(vector<int>& coins, int amount ...

最新文章

  1. 这38篇原创文章,带我入门深度学习!
  2. 中文ocr识别通过crnn
  3. Java网络02基本Web概念
  4. js异步解决方案 --- 回调函数 vs promise vs generater/yield vs async/await
  5. Unity导入免费的素材资源
  6. 梯度下降优化算法Momentum
  7. see into/see off/seek to等动词词组
  8. 山中无甲子,寒尽不知年
  9. 使用长角牛演示:防止ARP被攻击的原理和配置----------划水也别忘记学着玩
  10. 深度测评:贪心AI课程到底怎么样?
  11. 使用Pycharm关于自定义模块库的安装如douyin模块
  12. python打开桌面文件_python整理桌面文件
  13. 集成微控制器使太阳能微型逆变器设计成本有效
  14. Java API文档的使用详解
  15. js打开服务器word文件,javascript打开word文档的方法
  16. mongo DB数据库bindIP的配置和我的理解(bindIP不是应用服务器的IP)
  17. VMware虚拟机没有卸载干净无法重装的问题解决之道
  18. 图像处理:显著性区域检测总结(二)
  19. ECSHOP和SHOPEX如何选择?
  20. 执行报错MySQLSyntaxErrorException: Unknown database ‘eshop/‘的解决方法

热门文章

  1. 关于本人在多个技术平台发布文章的声明
  2. 教你玩转CSS 分组选择器和嵌套选择器
  3. ad10怎么挖铺的铜_黄金怎么验真假,简单易行方法多。
  4. mysql 字符集测试_MySQL多字符集备份恢复测试
  5. php制作简单的用户登陆,如何用php代码实现简单的用户登陆以及登陆验证功能
  6. 如何查看mysql的gtid_汇总丨MySQL GTID技术点,看这一篇就够了!
  7. activiti mysql 版本_Mysql8.0.17版本不能自动创建activiti表的坑
  8. 多路复用IO和异步IO
  9. 美团点评云真机平台实践
  10. 求助:安装程序无法创建一个DCOM用户帐号来注册.....\valec.exe