题解 | #兑换零钱(二)#

兑换零钱(二)

http://www.nowcoder.com/practice/521cead04d1442899767578c3aa395f0

题目分析

  1. 题目给出了一个target目标钱数,和一个nums数组,其中元素代表是硬币的币值
  2. 题目说不同硬币的币值可以无限次挑选
  3. 题目要求返回凑出target目标数的硬币挑选方案的数量

方法一:动态规划

  • 实现思路
    • 我们规定dp[i][j]表示在前i种硬币中挑选,凑出j的方案数,最终返回结果是dp[len(nums)][target]
    • 因此对于每一个dp[i][j]我们知道
      • i个硬币选择0个,有dp[i][j] = dp[i-1][j]
      • i个硬币选择1个,有dp[i][j] = dp[i-1][j-nums[i-1]]
      • i个硬币选择k个,有dp[i][j] = dp[i-1][j-k*nums[i-1]]
    • 状态转移方程有
    dp[i][j]=k=0k×nums[i]jdp[i1][jk×nums[i1]]dp[i][j] = \sum_{k=0}^{k×nums[i]\le j}dp[i-1][j-k×nums[i-1]]
    • 初始化条件dp[0][0]=1,对于0种硬币凑出0价值,只有一种方案

#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param target int整型 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    def change(self , target: int, nums: List[int]) -> int:
        # write code here
        dp = [[0]*(target+1) for _ in range(len(nums)+1)]
        dp[0][0] = 1 # 用前0个元素凑0元有且只有一种方法
        for i in range(1, len(nums) + 1):
            value = nums[i-1]
            for j in range(target + 1):
                k = 0 # 挑选value的硬币数量
                while k*value <= j:
                    dp[i][j] += dp[i-1][j-k*value]    # 迭代将挑新的硬币数量为0,1,2...都统计起来
                    k += 1
        return dp[len(nums)][target]

复杂度分析

  • 时间复杂度:O(target2×n)O(target^2×n),其中targettarget是目标凑出来的值,nn是硬币数组的总长度
  • 空间复杂度:O(target×n)O(target×n),其中targettarget是目标凑出来的值,nn是硬币数组的总长度

方法二:动态规划+时间优化

  • 实现思路
    • 由于上一个动态规划中,我们有一部分代价在迭代相加第i种硬币选择k数量的情况,这造成了很大的时间开销
    • 我们可以发现dp[i][j-nums[i-1]]就能代表所有的相加之和的部分。具体理解起来就是,由于我们的硬币挑选可以无限次,所以我们不要直接去找dp[i-1]的部分,dp[i][j-nums[i-1]]表示我在挑选了一枚第i个硬币后,我还可以在前i个硬币中继续挑选,因此省去了大量将k=1,2,...相加的工作
    • 数学推导如下
      • dp[i][j] = dp[i-1][j] + dp[i-1][j-v] + dp[i-1][j-2v] + ... + dp[i-1][j-kv])
      • dp[i][j-v] = dp[i-1,[j-v] + dp[i-1][j-2v] + ... + dp[i-1][j-kv])
    • 因此dp[i][j] = dp[i-1][j] + dp[i][j-v]
    dp[i][j]=dp[i1][j]+dp[i][jv]        if:j>=vdp[i][j] = dp[i-1][j] + dp[i][j-v] \;\;\;\;if:j>=v

alt

#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param target int整型 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    def change(self , target: int, nums: List[int]) -> int:
        # write code here
        dp = [[0]*(target+1) for _ in range(len(nums)+1)]
        # 前0,1,2...,len(nums)个元素凑出0的方案为1种
        for i in range(len(nums)+1):
            dp[i][0] = 1
        for i in range(1, len(nums)+1):
            value = nums[i-1]
            for j in range(1, target+1):
                if j >= value:
                    dp[i][j] = dp[i-1][j] + dp[i][j-value]	# dp[i][j-value]表示虽然减去了一次挑选value值硬币的尝试,但是我还可以在前i种硬币中继续挑选,因此解决了可以无限次挑选同一种硬币的问题
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[len(nums)][target]

复杂度分析

  • 时间复杂度:O(target×n)O(target×n),其中targettarget是目标凑出来的值,nn是硬币数组的总长度
  • 空间复杂度:O(target×n)O(target×n),其中targettarget是目标凑出来的值,nn是硬币数组的总长度

方法三:动态规划+时间优化+空间优化(滚动数组)

  • 实现思路
    • 因为我们看到dp转移的过程中当前行的状态只跟前一行有关系,因可以用滚动数组的方式减少空间开销

    • 将二维数组转化为一维数组,具体实现可以与方法二对比

#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param target int整型 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    def change(self , target: int, nums: List[int]) -> int:
        # write code here
        dp = [0]*(target+1) 
        # 前0,1,2...,len(nums)个元素凑出0的方案为1种
        dp[0] = 1
        for i in range(1, len(nums)+1):
            value = nums[i-1]
            for j in range(value, target+1):
                dp[j] = dp[j] + dp[j-value]
        return dp[target]

复杂度分析

  • 时间复杂度:O(target×n)O(target×n),其中targettarget是目标凑出来的值,nn是硬币数组的总长度
  • 空间复杂度:O(target)O(target),其中targettarget是目标凑出来的值,nn是硬币数组的总长度,二维数组空间代价用一维数组滚动数组代替
全部评论
本来认为自己懂点代码,看完你的后。我决定删库跑路
点赞 回复 分享
发布于 2022-06-23 20:42

相关推荐

头像
04-27 15:11
已编辑
华东师范大学 算法工程师
暑期实习从2月开始投,面了两个月,流程该挂的都挂完了,腾讯字节一共号称是1.7w个hc,不知道都发给谁了,估计今年秋招要难顶。Timeline米哈游、美团、蚂蚁、微软等公司直接简历挂穿,没进面。携程:3.3&nbsp;投递、测评3.12&nbsp;笔试3.18&nbsp;一面3.25&nbsp;二面4.13&nbsp;ai面(hr面)4.14&nbsp;英语测评4.23&nbsp;offer(已拒)腾讯:2.6&nbsp;测评2.28&nbsp;wxg一面3.5&nbsp;wxg二面(挂)3.11&nbsp;teg一面3.21&nbsp;teg二面(取消)3.31&nbsp;teg一面4.10&nbsp;teg二面(挂)4.21&nbsp;wxg一面4.24&nbsp;wxg二面(挂)字节:1.28&nbsp;aml约面(取消)3.17&nbsp;火山一面(挂)4.8&nbsp;aml一面(挂)4.20&nbsp;抖音data一面(挂)阿里:3.23&nbsp;投递、测评3.28&nbsp;笔试3.31&nbsp;淘天一面4.8&nbsp;钉钉一面4.9&nbsp;淘天二面4.10&nbsp;阿里控股一面4.12&nbsp;钉钉二面(取消)4.15&nbsp;淘天hr面4.16&nbsp;淘天offer(已接)4.21&nbsp;高德一面(取消)4.22&nbsp;淘宝闪购一面(取消)面试最大的感触是,现在撞上ai转型,一堆老业务急着转向,新业务非常不成熟,研究型的组bar非常高根本进不去,业务侧挂着算法的岗位干的都是工程活,面试却又要问算法,另外agent的落地也远没有那么广,绝大多数还是那套写死的系统调一下llm&nbsp;api或者做做rag,其余少部分真的在搭agent的,基本不能在线上服务用什么很智能的模型,现阶段成本太高,进去大概率就是给垃圾模型从工程方面兜底,除了业务场景的应用和数据经验以外,技术方面很难有什么提升。算法岗做不了基模的还是去搜广推好,之前判断失误了完全没投,秋招不知道还进不进得去。
绿糖滑稽:携程这什么雷霆流程时长
我的求职进度条
点赞 评论 收藏
分享
03-31 14:46
已编辑
门头沟学院 Web前端
励志成为双港第一ja...:这其实很正常,离的太远了,他认为你不会来,就为了混个面试,而且成本很高,实习生都优先选本地高校。吃了地域的亏,所有很多时候地域可能比院校层次更重要。
点赞 评论 收藏
分享
评论
7
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务