image.png

其实我动态规划不是很会,但是我们可以先试用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

北语张益达
6 声望4 粉丝