leetcode[377]组合总和 Ⅳ (动态规划,完全背包)

# 给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 
# 
#  题目数据保证答案符合 32 位整数范围。 
# 
#  
# 
#  示例 1: 
# 
#  
# 输入:nums = [1,2,3], target = 4
# 输出:7
# 解释:
# 所有可能的组合为:
# (1, 1, 1, 1)
# (1, 1, 2)
# (1, 2, 1)
# (1, 3)
# (2, 1, 1)
# (2, 2)
# (3, 1)
# 请注意,顺序不同的序列被视作不同的组合。
#  
# 
#  示例 2: 
# 
#  
# 输入:nums = [9], target = 3
# 输出:0
#  
# 
#  
# 
#  提示: 
# 
#  
#  1 <= nums.length <= 200 
#  1 <= nums[i] <= 1000 
#  nums 中的所有元素 互不相同 
#  1 <= target <= 1000 
#  
# 
#  
# 
#  进阶:如果给定的数组中含有负数会发生什么?问题会产生何种变化?如果允许负数出现,需要向题目中添加哪些限制条件? 
#  Related Topics 动态规划 
#  👍 379 👎 0

# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0] * (target+1)
        dp[0] = 1
        for i in range(1, target+1):
            for num in nums:
                if num <= i:
                    dp[i] += dp[i - num]
        return dp[target]
# leetcode submit region end(Prohibit modification and deletion)
				Success:
				Runtime:48 ms, faster than 75.13% of Python3 online submissions.
				Memory Usage:14.8 MB, less than 82.01% of Python3 online submissions.

错误解法——递归, 超时

看到这题时,第一反应是用递归:

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        count = 0
        if target == 0:
            count = 1
        elif target > 0:
            for num in nums:
                count += self.combinationSum4(nums, target-num)
        return count

果断超时。

用例:
[4,2,1], 32

本地跑了很久,得到结果是:
39882198

动态规划

递归之所以超时,是因为递归层数太深,已经有的知识没有重复利用。

记录已有信息,从底向上计算,即用到动态规划法。具体来说,该问题属于完全背包问题,即放置的物品可重复使用。

用 dp[i] 表示target为i时的所有组合数,考虑该怎么各dp[k] (k < i) 推出dp[i]呢?

假设最后一个加入的数字是num,且加入num 之后可得到一个元素之和等于 i 的排列,那么就可以把dp[i-num]种组合都累加到最终结果中。求出所有的 dp[i−num] 之和,即等于dp[i]。

可得递推公式:

dp[i] = 1, i = 0
dp[i] = SUM(dp[i-num[k]]), 0 <= k < n, i > 0
dp[i] = 0,i < 0

有了递推公式,动态规划就基本完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值