其实我动态规划不是很会,但是我们可以先试用dfs来解决这个问题,然后使用一个数组保存已经搜索到的转态。其实也是动态规划的思想,只不过一个是递推一个是递归,相对于递推而言,递归效率稍差。
思路:对于第i个硬币,取多少个。
dp_i_j代表的是,第i个硬币,在还剩j需要取的时候,有多少种选择。
1.记忆化深度优先搜索
`
class Solution(object):
def waysToChange(self, n):
"""
:type n: int
:rtype: int
"""
changes = [25,10,5,1]
dp = [[-1]*(n+1) for _ in range(len(changes))]
def dfs(i,left):
if dp[i][left]!=-1:
return dp[i][left]
res = 0
if left==0 or i==len(changes)-1:
return 1
for num in range(0,left//changes[i]+1):
res += dfs(i+1,left-num*changes[i])
return res
return dfs(0,n)
`
下面改成 非递归形式的。而且我们注意到,这个二维数组其实可以压缩成一维的。因为每次都是只用到了上一层左边的信息,所以我们从右边到左边遍历,保证每次使用到的都是还没有被更新的(上一层的)数据。
class Solution(object):
def waysToChange(self, n):
"""
:type n: int
:rtype: int
"""
if n==0:return 0
changes = [1,5,10,25]
# init 因为此时只有一种硬币可以拿,而且是1,所以无论如何你都仅有一种选择。就是全拿1
dp = [1 for i in range(n+1)]
# 第 i 种零钱
for i in range(1,len(changes)):
# 还剩0~j的钱
for j in range(n,-1,-1):
temp = 0
# 拿多少张 第 i 种零钱
for t in range(0,j//changes[i]+1):
temp+=dp[j-t*changes[i]]
dp[j] = temp
return dp[-1]%1000000007
这里我提交了,但是超时了。于是思考下 是否时间复杂度也能优化一下?
思考了一下,发现在第i个硬币且还剩j0零钱的时候,我们判断取1,2,3,4....k个硬币的情况(k为当前所剩的零钱能取的最多硬币数目)
假设我们取了一个硬币然后零钱变成了j1,那么我们在遍历dpi的时候,他不取硬币的情况其实和刚刚还剩j1 的时候取1个硬币的情况一样。但是这两个状态我们却重复计算了。所以我们优化的思路来了
刚刚的通俗的解释用公式可以具体表示如下:(f(i,j)的意义等同于刚开始的dp[i][j])
f(i,v)=f(i−1,v)+f(i−1,v−ci)+f(i−1,v−2ci)⋯f(i−1,v−kci)f(i,v−ci)=f(i−1,v−ci)+f(i−1,v−2ci)+f(i−1,v−3ci)⋯f(i−1,v−kci)
共 kk 项。注意到上面两个方程中标成红色的 kk 项是完全相同的,于是我们可以用下面式子的左半部分
f(i,v)=f(i−1,v)+f(i,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 i in range(coin, n + 1):
f[i] += f[i - coin]
return f[n] % mod
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。