LeetCode 刷題筆記 - 322. 零錢兌換

難度:

中等編程

描述:

給定不一樣面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算能夠湊成總金額所需的最少的硬幣個數。若是沒有任何一種硬幣組合能組成總金額,返回 -1。swift

示例:

1:
輸入: coins = [1, 2, 5], amount = 11
輸出: 3 
解釋: 11 = 5 + 5 + 1
複製代碼
2:
輸入: coins = [2], amount = 3
輸出: -1
複製代碼

說明: 你能夠認爲每種硬幣的數量是無限的。bash

來源:力扣(LeetCode) 連接:leetcode-cn.com/problems/co… 著做權歸領釦網絡全部。商業轉載請聯繫官方受權,非商業轉載請註明出處。網絡


語言:

swift函數

解析:

這是一道比較基礎的關於動態規劃的題。那咱們就按着動態規劃的標準解題步驟來一步一步解答。
咱們須要拆分子問題,找到狀態轉移方程。
首先拆分子問題:ui

1. 如何用最少的 coins 湊夠 0 元?
2. 如何用最少的 coins 湊夠 1 元?
3. 如何用最少的 coins 湊夠 2 元?
4. ...
5. 如何用最少的 coins 湊夠 n 元?
複製代碼

咱們分別解決這些子問題,咱們用d(i) = j來表示,湊夠i元最少須要j個硬幣:spa

1. 如何用最少的 coins 湊夠 0 元?
複製代碼

對於該狀況,顯然是須要0枚硬幣就能湊夠0元,故:code

d(0) = 0
複製代碼
2. 如何用最少的 coins 湊夠 1 元?
複製代碼

爲了湊夠1元,咱們須要1枚1元的硬幣,同時咱們要和上一個問題想想有沒有什麼關聯?爲了解決子問題2,就等於先解決了子問題1,在解決了子問題1的基礎解決了子問題2。leetcode

d(1) = d(1 - 1) + 1 = d(0) + 1 = 0 + 1 = 1
// d(1) 表明爲了湊夠 1 元
// d(1 - 1) + 1 表明爲了解決這個問題,咱們須要解決湊夠 0 元狀況下的結果,再加上湊夠 1 元須要的一塊的這一種結果
複製代碼
3. 如何用最少的 coins 湊夠 2 元?
複製代碼

爲了求出最少湊夠2元,咱們一樣能夠得出:解決問題2,湊夠1元的狀況下,再湊夠1元:get

d(2) = d(2 - 1) + 1 = d(1) + 1 = 1 + 1 = 2
// d(2) 表明爲了湊夠 2 元
// d (2 - 1) + 1 表明爲了解決這個問題,咱們須要解決湊夠 1 元的狀況下,再加上湊夠剩下的 1 元
複製代碼

可是題目上,咱們不只有1元的硬幣,還有2元的硬幣,這個時候,咱們僅須要一枚2元的硬幣就能夠湊夠2元,也就是:湊夠 2 元 = 湊夠 0 元 + 湊夠 2 元

d(2) = d(2 - 2) + 1 = d (0) + 1 = 0 + 1 = 1
// d(2) 表明爲了湊夠 2 元
// d(2 - 2) + 1 表明能夠將湊夠二元分解爲,先湊夠 0 元,再用 1 個 2 元的硬幣來湊夠 2 元
複製代碼

其實在這個時候,d(2) = 1,能夠將上面的兩個方程簡化一下爲:
d(2) = min{d(2 - 1) + 1, d(2 - 2) + 1}
那麼咱們就能夠抽象成如下狀態轉移方程:
d(n) = min{d(n - c) + 1}
意思爲,爲了湊夠 n 元,咱們須要找到不一樣的面值 c 下的最優解。
因此爲了湊夠 11 元,本題來講,咱們須要以下狀態方程:

d(11) = min{d(11 - 1) + 1, d(11 - 2) + 1, d(11 - 5) + 1}
      = min{d(10) + 1, d(9) + 1, d(6) + 1}
d(10) = ...
d(9) = ...
d(6) = ...
...
複製代碼

編程的話,也就是須要將狀態轉移方程表達出來。咱們須要一個dict來存儲湊夠不一樣價格時候的最優解。能夠循環的條件爲amount - coin >= 0,舉例爲若是硬幣只有10元面值,而須要湊夠5元,這時候5 - 10 < 0,不知足條件。

代碼以下:

class Solution {
    func coinChange(_ coins: [Int], _ amount: Int) -> Int {
        if amount == 0 {
            return 0
        }
        var dict = [Int : Int]()
        dict[0] = 0
        for index in 1...amount {
            var minLength = -1
            for coinsIndex in 0...coins.count - 1 {
                let previousValue = index - coins[coinsIndex]
                if previousValue >= 0 {
                    if (dict[previousValue] != nil) && dict[previousValue]! != -1 {
                        if minLength == -1 {
                            minLength = dict[previousValue]! + 1
                        } else {
                            minLength = min(minLength, dict[previousValue]! + 1)
                        }
                    }
                }
            }
            dict[index] = minLength
        }
        return dict[amount]!
    }
}
複製代碼

總結

動態規劃頗有趣,須要好好研究一下。

相關文章
相關標籤/搜索