Leetcode 39. 组合总和

文章讨论了如何在回溯算法中解决完全背包问题,强调了通过控制候选集合来避免重复子集选取的重要性,并给出了两种解法:组合回溯和排列回溯结合重复元素记录。作者推荐使用组合树方法优化复杂度。
摘要由CSDN通过智能技术生成

在这里插入图片描述

心路历程:

这道题就差把回溯算法写在题面上了,其实这道题如果不是要遍历所有的可能情况,而是求某个最大最小值的话,就是一道经典的完全背包问题了。
这道题有一个注意的点,就是如何通过‘控制候选集合’来实现‘不重复子集’,第一反应是遍历到重复的不添加,但是这样做其实很费时间复杂度。最好的做法应该是用组合树去做,在每个状态下同时维护当前的候选集合,从而避免重复元素的选取。

回溯的这些排列/组合/子集问题,树形的区别其实就在于候选集合的维护上面,一个经典的方法就是用一个start_index记录当前结点可供选择的候选集合。这块labuladong的文章讲的很好。如果实在忘了,也可以按照全部遍历去掉重复的思路,就是很麻烦。

解法一:组合回溯

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        path = []
        res = []
        def dfs(current_target, start):
            if current_target == 0:
                res.append(path[:])
                return
            if current_target < 0:
                return
            for i in range(start, len(candidates), 1):
                if current_target - candidates[i] >= 0:
                    path.append(candidates[i])
                    dfs(current_target - candidates[i], i)  # 一个从根节点的分支把所有包含i的情况都搜索出来,因为i无限次拿取
                    path.pop()
        dfs(target, 0)
        return res

解法二:排列回溯+记录重复

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        path = []
        res = []
        visited = []  # 存储n元组,n的数量于candidates的大小一致
        ntuple = [0] * len(candidates)
        def dfs(current_target):
            if current_target == 0:
                if ntuple not in visited:
                    res.append(path[:])
                    visited.append(ntuple[:])
                return
            if current_target < 0:
                return
            for i, each in enumerate(candidates):
                if current_target - each >= 0:
                    path.append(each)
                    ntuple[i] += 1
                    dfs(current_target - each)
                    ntuple[i] -= 1
                    path.pop()
        dfs(target)
        return res
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值