【算法】回溯算法专题② ——组合型回溯 + 剪枝 python

目录

  • 前置知识
  • 进入正题
  • 小试牛刀
  • 实战演练
  • 总结

  • 前置知识

    【算法】回溯算法专题① ——子集型回溯 python


    进入正题

    组合https://leetcode.cn/problems/combinations/submissions/596357179/

    给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

    你可以按 任何顺序 返回答案。

    示例 1:

    输入:n = 4, k = 2
    输出:
    [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4],]

    示例 2:

    输入:n = 1, k = 1
    输出:[[1]]

    提示:
    1 <= n <= 20
    1 <= k <= n

    思路:

    回溯思路(选或不选 / 枚举选哪个)
    剪枝(选不满k个就停止递归)

    code1:

    class Solution:
        def combine(self, n: int, k: int) -> List[List[int]]:
            ans = []
            path = []
    
            def dfs(i):
                # 剪枝
                if n - i + 1 + len(path) < k:  
                    return
    
                if len(path) == k:
                    ans.append(path.copy())
                    return
                # 不选
                dfs(i + 1)
    
                # 选
                path.append(i)
                dfs(i + 1)
                path.pop()
    
            dfs(1)
            return ans
    

    code2:

    class Solution:
        def combine(self, n: int, k: int) -> List[List[int]]:
            ans = []
            path = []
    
            def dfs(i):
                # 剪枝
                if n - i + 1 + len(path) < k:
                    return
                if len(path) == k:
                    ans.append(path.copy())
                    return
                # 枚举选哪个    
                for j in range(i, n + 1):
                    path.append(j)
                    dfs(j + 1)
                    path.pop()
    
            dfs(1)
            return ans
    


    小试牛刀

    组合总和Ⅲ https://leetcode.cn/problems/combination-sum-iii/description/

    找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
    1.只使用数字1到9
    2.每个数字 最多使用一次
    返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

    示例 1:

    输入: k = 3, n = 7
    输出: [[1,2,4]]
    解释:
    1 + 2 + 4 = 7
    没有其他符合的组合了。

    示例 2:

    输入: k = 3, n = 9
    输出: [[1,2,6], [1,3,5], [2,3,4]]
    解释:
    1 + 2 + 6 = 9
    1 + 3 + 5 = 9
    2 + 3 + 4 = 9
    没有其他符合的组合了。

    示例 3:

    输入: k = 4, n = 1
    输出: []
    解释: 不存在有效的组合。
    在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

    提示:
    2 <= k <= 9
    1 <= n <= 60

    思路:

    与上题一样的思路
    回溯 + 剪枝

    题解1:

    class Solution:
        def combinationSum3(self, k: int, n: int) -> List[List[int]]:
            ans = []
            path = []
    
            def dfs(i):
            	# 剪枝
                if len(path) + 10 - i < k:
                    return
                if sum(path) > n:
                    return
                    
                if len(path) == k and sum(path) == n:
                    ans.append(path.copy())
                    return
                # 选
                path.append(i)
                dfs(i + 1)
                path.pop()
    
                # 不选
                dfs(i + 1)
    
            dfs(1)
            return ans
    

    题解2:

    class Solution:
        def combinationSum3(self, k: int, n: int) -> List[List[int]]:
            ans = []
            path = []
    
            def dfs(i):
                # 剪枝
                if len(path) + 10 - i < k:
                    return
                if sum(path) > n:
                    return
                    
                if len(path) == k and sum(path) == n:
                    ans.append(path.copy())
                    return
    			# 枚举选哪个
                for j in range(i, 10):
                    path.append(j)
                    dfs(j + 1)
                    path.pop()
    
            dfs(1)
            return ans
    

    当然,我们可以在判断和是否为n时进行优化:
    在dfs中传入target参数,每选一个数字 j 就把target减去 j

    code:

    class Solution:
        def combinationSum3(self, k: int, n: int) -> List[List[int]]:
            ans = []
            path = []
    
            def dfs(i, t):
                if len(path) + 10 - i < k:
                    return
                if t < 0:
                    return
                if len(path) == k and t == 0:
                    ans.append(path.copy())
                    return
    
                for j in range(i, 10):
                    path.append(j)
                    dfs(j + 1, t - j)
                    path.pop()
    
            dfs(1, n)
            return ans
    


    实战演练

    括号生成 https://leetcode.cn/problems/generate-parentheses/

    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

    示例 1:

    输入:n = 3
    输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]

    示例 2:

    输入:n = 1
    输出:[“()”]

    提示:
    1 <= n <= 8

    思路:

    理解为填左括号, 不选理解为填右括号

    题解:

    class Solution:
        def generateParenthesis(self, n: int) -> List[str]:
            ans = []
            path = []
    
            def dfs(i, left, right):
                if i == 2 * n:
                    ans.append("".join(path))
                    return
                # 左括号数量不能超过n
                if left < n:
                    path.append("(")
                    dfs(i + 1, left + 1, right)
                    path.pop()
                # 右括号数量不能超过左括号数量
                if right < left:
                    path.append(")")
                    dfs(i + 1, left, right + 1)
                    path.pop()
    
            dfs(0, 0, 0)
            return ans
    


    总结

    剪枝是一种优化技术,用于提前终止那些不可能找到解的搜索路径,从而提高算法效率。
    而组合型回溯问题常常与剪枝相结合


    END
    如果有更多问题或需要进一步的帮助,可以在评论区留言讨论哦!
    如果喜欢的话,请给博主点个关注 谢谢

    作者:查理零世

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【算法】回溯算法专题② ——组合型回溯 + 剪枝 python

    发表回复