中等編程
給定不一樣面額的硬幣 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]!
}
}
複製代碼
動態規劃頗有趣,須要好好研究一下。