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

兑换零钱(二)

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

相关推荐

关于“实习生工资多少才算正常”,其实并没有一个放之四海而皆准的标准,但如果结合一线城市的生活成本、工作强度以及实习本身创造的价值来看,我个人认为6000&nbsp;元左右应当是一个基本及格线,也就是每天&nbsp;200&nbsp;多元。如果能达到&nbsp;300、400&nbsp;元一天,甚至更高,那无疑是更理想的状态。首先,从现实成本看,房租、通勤、餐饮几乎都是刚性支出。低于这个水平的实习,往往意味着实习生需要用家庭或存款“倒贴”工作,这在长期来看并不合理。实习本质上是学习,但并不等于“廉价劳动力”,更不应该是经济压力的来源。其次,愿意给实习生更高薪资的公司,通常不会是差公司。这至少说明两点:一是公司资金相对充足,不是靠压缩人力成本勉强维持;二是公司认可实习生的价值,希望你真正参与业务、创造产出,而不是只做边角料工作。很多高薪实习往往伴随着更规范的培养体系、更高的信息密度和更真实的项目经验。当然,高工资并不等于一切,但它往往是一个重要信号。能给到&nbsp;300、400&nbsp;元一天甚至更多的公司,往往对效率、能力和长期发展更有追求,也更可能处在一个有前景的赛道中。总结来说,实习工资不仅是钱的问题,更是公司态度、实力和发展前景的体现。在条件允许的情况下,争取一份“付得起你时间”的实习,本身就是一种理性选择。
北国牛马:你是不是忘了你一周只能上五天班,月薪6000那你日薪就得300了,日薪200一个月也就4000,也就刚好覆盖生活成本了
实习生工资多少才算正常?
点赞 评论 收藏
分享
评论
7
1
分享

创作者周榜

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