组合总和(系列)

前几天动态规划,刚做出一点感觉,今天就变成回溯了,惆怅

组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 
class Solution:
    import bisect
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        current_list = []
        candidates.sort()
        end = bisect.bisect_right(candidates, target)

        def backtrack(numsum, startindex):
            if numsum == target:
                res.append(current_list[:]) # 复制内容并指向新的内存地址
            else:
                for i in range(startindex, end):
                    numsum += candidates[i]
                    if numsum > target:
                        numsum -= candidates[i]
                        break
                    current_list.append(candidates[i])
                    backtrack(numsum, i)
                    numsum -= candidates[i]
                    current_list.pop()

        backtrack(0, 0)
        return res

细节需要注意:res.append(current_list[:]),不能使用res.append(current_list)。
区别是:前者复制内容并指向新的内存地址,后者是浅复制,只是指向 current_list 的内存地址,因为后面还有对 current_list 的操作所以只能使用前一种。

组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。 
class Solution:
    import bisect
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        if sum(candidates) < target:
            return []
        res = []
        current_list = []
        candidates.sort()
        end = bisect.bisect_right(candidates, target)

        def backtrack(beginindex, numsum):
            if numsum == target and current_list not in res: # 增加了判断条件
                res.append(current_list[:])
            else:
                for i in range(beginindex, end):
                    numsum += candidates[i]
                    if numsum>target:
                        numsum -= candidates[i]
                        break
                    current_list.append(candidates[i])
                    backtrack(i+1, numsum) # 改动的地方
                    numsum -= candidates[i]
                    current_list.pop()
        
        backtrack(0, 0)
        return res

直接根据 I 改的,改变了两处:

  1. 由于有重复元素,会有重复组合,所以增加了 current_list not in res 的判断条件;
  2. 将 backtrack(i, numsum) 改成 backtrack(i+1, numsum)。

组合总和 III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。
解集不能包含重复的组合。 
class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        nums = list(range(1, 10))
        res = []
        currentlist = []
        
        def backtrack(begin, numsum, count):
            if numsum==n and count==k:
                res.append(currentlist[:])
            else:
                for i in range(begin, 9):
                    numsum += nums[i]
                    if count>=k or numsum>n:
                        numsum -= nums[i]
                        break
                    currentlist.append(nums[i])
                    count += 1
                    backtrack(i+1, numsum, count)
                    count -= 1
                    numsum -= nums[i]
                    currentlist.pop()
        
        backtrack(0, 0, 0)
        return res

换汤不换药,只是增加一个 count 用于固定相加的数为 k

组合总和 Ⅳ

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

还想用回溯,结果一个测试集 [4,2,1] 32 就翻车了哈哈

做了这么多动态规划,为啥换了一题我又想不出状态转移方程了/(ㄒoㄒ)/~~

看完官方解答只能赞叹:牛蛙牛蛙,这都能 dp ?!!!

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [1] + [0] * target
        for i in range(1, target + 1):
            for num in nums:
                if num <= i:
                    dp[i] += dp[i - num]
        
        return dp[target]

用 dp[x] 表示选取的元素之和等于 x 的方案数,目标是求 dp[target]。

动态规划的边界是 dp[0]=1。只有当不选取任何元素时,元素之和才为 0,因此只有 1 种方案。

当 1≤i≤target 时,如果存在一种排列,其中的元素之和等于 i,则该排列的最后一个元素一定是数组 nums 中的一个元素。假设该排列的最后一个元素是 num,则一定有 num≤i,对于元素之和等于 i−num 的每一种排列,在最后添加 num 之后即可得到一个元素之和等于 i 的排列,因此在计算 dp[i] 时,应该计算所有的 dp[i−num] 之和。

由此可以得到动态规划的做法:

初始化 dp[0]=1;

遍历 iii 从 111 到 target,对于每个 iii,进行如下操作:
    遍历数组 nums 中的每个元素 num,当 num≤i 时,将 dp[i−num] 的值加到 dp[i]。

最终得到 dp[target] 的值即为答案。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值