中等编程
给定不一样面额的硬币 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]!
}
}
复制代码
动态规划颇有趣,须要好好研究一下。