【LeetCode每周算法】零钱兑换
題目來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/coin-change
給你一個整數數組 coins ,表示不同面額的硬幣;以及一個整數 amount ,表示總金額。
計算并返回可以湊成總金額所需的 最少的硬幣個數 。如果沒有任何一種硬幣組合能組成總金額,返回?-1 。
你可以認為每種硬幣的數量是無限的。
示例?1:
輸入:coins = [1, 2, 5], amount = 11
輸出:3?
解釋:11 = 5 + 5 + 1
示例 2:
輸入:coins = [2], amount = 3
輸出:-1
示例 3:
輸入:coins = [1], amount = 0
輸出:0
示例 4:
輸入:coins = [1], amount = 1
輸出:1
示例 5:
輸入:coins = [1], amount = 2
輸出:2
?
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
分析
一些分析內容,借鑒自博客:https://juejin.cn/post/6985150114134229022
怎么感覺像是回到了小學應用題?
1、暴力窮舉逐個排除
簡單分析一下: 最小硬幣組合 -> 盡量用大的硬幣
這傻不拉幾的題誰出的,這么簡單
????????7+7+7=21,21+2+2+2=27, 6枚硬幣
臥槽
????????7+5+5+5+5=27, 5枚硬幣
我們可以很暴力的想一想,從大往小的算,可以加起來不超過27,比如
????????7+7+7+7 > 27 (排除)
????????7+7+7+5 或者 7 +7 +7+2+2+2 6枚
????????....
窮舉太慢了,還會涉及到很多的重復計算,如果我想要27以內的任何值最小的硬幣組合呢,想想都頭大了吧。
既然計算機可以通過內存保存之前的內容,又快,很明顯,我們開一個數組來保存之前的狀態。
2.動態規劃分析
雖然我們不知道最優策略是什么,但是最優策略肯定是K枚硬幣,a1, a2, ....ak面值加起來是27
所以一定有一枚最后的硬幣:ak.? 除掉這么硬幣,前面硬幣的面值加起來是27-ak
關鍵點1:
- 我們不關心前面的k-1枚硬幣是怎么拼出27-ak的(可能有一種拼法,也可能有100種拼法),而且我們現在甚至還不知道ak和K, 但是我們確定前面的硬幣拼出了27-ak
關鍵點2:
- 因為是最優策略, 所以拼出27-ak的硬幣數一定要最少,否則這就不是最優策略了
子問題
- 所以我們就要求:最少用多少枚硬幣可以拼出27-ak
- 原問題是最少用多少枚硬幣拼出27
- 我們將原問題轉化了一個子問題,而且規模更小:27-ak
- 為了簡化定義, 我們設狀態f(x)=最少用多少枚硬幣拼出x
等等,我們還不知道最后的那枚硬幣ak是多少
所以使用最少的硬幣數=f(27) = min{f(27-2)+1, f(27-5)+1, f(27-7)+1}
2.1遞歸解法
private static Map<Integer, Integer> cache = new HashMap<>();static {// 退出條件cache.put(2, 1);cache.put(3, -1);cache.put(4, 2);cache.put(5, 1);cache.put(6, 3);cache.put(7, 1);}// 2,5,7三種硬幣public static int minCoin(int target) {if (target < 2) {return -1;}Integer integer = cache.get(target);if (integer != null) {return integer;}// if (target == 2) { // return 1; // } // if (target == 3) { // return -1; // } // if (target == 4) { // return 2; // } // if (target == 5) { // return 1; // } // if (target == 6) { // return 3; // } // if (target == 7) { // return 1; // }int[] result = {minCoin(target - 2) + 1,minCoin(target - 5) + 1,minCoin(target - 7) + 1};int min = min(result);cache.put(target, min);return min;}public static int min(int[] valArr) {if (valArr == null || valArr.length < 1) {return Integer.MIN_VALUE;}int val = Integer.MIN_VALUE;for (int i : valArr) {if (i > 0) {val = val > 0 ? Math.min(val, i) : i;}}return val;}2.2動態規劃
轉移方程:
- 設狀態f(x)=最少用多少枚硬幣拼出x
- 對于任意的x : f(X) = min{f(X-2)+1, f(X-5)+1, f(X-7)+1}
初始條件和邊界情況:
- 提出問題
- x-2, x-5, x-7 小于0怎么辦?
- 什么時候停下來?
如果不能拼出Y, 就定義f[Y] = 正無窮
例如f[-1], f[-2] = 正無窮
例如:拼不出f[1]=min{f(-1)+1, f(-3)+1, f(-6)+1}
初始條件:f[0] = 0
計算順序
計算:f[1], f[2], ... f[27]
當我們計算到f[x], f[x-2], f[x-5], f[x-7] 都已經得到結果了。如圖:
上圖7只需要一個硬幣。
f[x] = 最少用多少枚硬幣拼出x
f[x] = ∞ 表示無法用硬幣拼出x
public static int coinChange(int [] A, int M ) {int[] f = new int[M+1];int n = A.length;f[0] = 0;int i,j;for (i = 1; i<=M; i++) {f[i] = Integer.MAX_VALUE;for (j = 0; j< n;j++) {// 邊界條件判斷if (i >= A[j] && f[i - A[j]] != Integer.MAX_VALUE) {f[i] = Math.min(f[i - A[j]] + 1, f[i]);// System.out.println(i + "=" +f[i]);}}}if (f[M] == Integer.MAX_VALUE) {f[M] = -1 ;}return f[M]; }總結
以上是生活随笔為你收集整理的【LeetCode每周算法】零钱兑换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LeetCode每周算法】两数相加
- 下一篇: 【动态规划】不信看完你还不懂动态规划