感觉是我学一辈子也想不出的算法,还是记录一下吧。
- 题目描述:
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
- 题解:
状态转移公式:
简化过得公式就是用前 i 种硬币凑成 v 分的方法数 = 用前 i - 1 种硬币凑成 v 分的方法数 + 用前 i 种硬币凑成 v - ci 的方法数,ci 是 第 i 种硬币的面值。
然而方法竟然还能简化到只用一行大小为 n+1 的数列(记为F)来计算:注意硬币要从大到小排列,因为 本轮的 f(i, v) (即 F[v]) 是由上一轮的 f(i - 1, v) (即 F[v]) 和 本轮的 f(i, v - ci)(即F[v - ci]),所以F[v]要在F[v-ci]之前更新。
代码
class Solution:
def waysToChange(self, n: int) -> int:
mod = 10**9 + 7
coins = [25, 10, 5, 1]
f = [1] + [0] * n
for coin in coins:
for v in range(coin, n + 1):
f[v] += f[v - coin]
return f[n] % mod
- 以 n = 25为例:
coin = 25:
v = 25: f[25] = f[25] + f[0] = 1(用25分的硬币凑成25分)
coin = 10:
v = 10: f[10] = f[10] + f[0] = 1(用10分25分硬币凑10分)
v = 11: f[11] = f[11] + f[1] = 0 (用10分25分硬币凑11分)
...
v = 20: f[20] = f[20] + f[10] = 1(用10分25分硬币凑20分)
...
v = 25: f[25] = f[25] + f[15] = 1(用10分25分硬币凑25分)
coin = 5:
i = 5: f[5] = f[5] + f[0] = 1(用5分10分25分硬币凑5分)
i = 6: f[6] = f[6] + f[1] = 0(用5分10分25分硬币凑6分)
...
i = 10:f[10] = f[10] + f[5] = 2(用5分10分25分硬币凑成10分)
i = 11:f[11] = f[11] + f[6] = 0 (用5分10分25分硬币凑成11分)
...
i = 15:f[15] = f[15] + f[10] = 2 (用5分10分25分硬币凑成15分)
...
i = 20:f[20] = f[20] + f[15] = 3
...
i = 25:f[25] = f[25] + f[20] = 4
coin = 1:
i = 1: f[1] = f[1] + f[0] = 1(用1分5分10分25分凑1分)
...
i = 5: f[5] = f[5] + f[4] = 2
...
i = 10: f[10] = f[10] + f[9] = 4
...
i = 15: f[15] = f[15] + f[14] = 6
...
i = 20: f[20] = f[20] + f[19] = 7
...
i = 25: f[25] = f[25] + f[24} = 11