• LeetCode 0216.组合总和 III:回溯(剪枝) OR 二进制枚举


    【LetMeFly】216.组合总和 III:回溯(剪枝) OR 二进制枚举

    力扣题目链接:https://leetcode.cn/problems/combination-sum-iii/

    找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

    • 只使用数字1到9
    • 每个数字 最多使用一次 

    返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

     

    示例 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

    方法一:二进制枚举(选与不选)

    一共 9 9 9个数,每个数选与不选一共有 2 9 = 512 2^9=512 29=512种情况。

    我们只需要使用一个二进制数一一枚举这 512 512 512种情况即可。

    二进制数的每一位代表每个数的选与不选,对于某种情况,只需要判断是否恰好为 k k k个数,以及是否恰好和为 n n n即可。

    • 时间复杂度 O ( C × 2 C ) O(C\times2^C) O(C×2C),其中 C = 9 C=9 C=9
    • 空间复杂度 O ( C ) O(C) O(C)

    AC代码

    C++
    class Solution {
    public:
        vector<vector<int>> combinationSum3(int k, int n) {
            vector<vector<int>> ans;
            int to = 1 << 9;
            for (int i = 0; i < to; i++) {
                if (__builtin_popcount(i) != k) {
                    continue;
                }
                vector<int> thisSolution;
                int thisCnt = 0;
                for (int j = 0; j < 9; j++) {
                    if (i & (1 << j)) {
                        thisCnt += j + 1;
                        thisSolution.push_back(j + 1);
                    }
                }
                if (thisCnt == n) {
                    ans.push_back(thisSolution);
                }
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    Python
    # from typing import List
    
    class Solution:
        def combinationSum3(self, k: int, n: int) -> List[List[int]]:
            ans = []
            for i in range(1 << 9):
                if i.bit_count() != k:  # Python 3.9.4中似乎无此函数
                    continue
                thisSolution = []
                thisCnt = 0
                for j in range(9):
                    if i & (1 << j):
                        thisCnt += j + 1
                        thisSolution.append(j + 1)
                if thisCnt == n:
                    ans.append(thisSolution)
            return ans
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    方法二:回溯+剪枝(DFS)

    写一个函数dfs(k, n, index)来求所有“从[index,9]范围内选k个数使得和为n”的情况。

    • 如果k = 0 && n == 0,则说明当前方案为一个可行方案,计入答案中且返回
    • 如果index > n || index == 10 || k <= 0,则终止(剪枝/递归终止条件)

    这样,就只有选与不选index这两种情况:

    1. 不选index:直接递归调用dfs(k, n, index + 1)
    2. index:将index加入当前选择方案的数组中、递归调用dfs(k - 1, n - index, index + 1)、将index从当前方案中移除(回溯)

    以上

    • 时间复杂度 O ( ( C k ) × k ) O((Ck)\times k) O((Ck)×k),其中 C = 9 C=9 C=9 ( C k ) (Ck) (Ck)为组合数从 C C C个数里面选 k k k
    • 空间复杂度 O ( C ) O(C) O(C)

    AC代码

    C++
    class Solution {
    private:
        vector<vector<int>> ans;
        vector<int> now;
    
        // 从[index,9]范围内选k个数使得和为n
        void dfs(int k, int n, int index) {
            if (!k && !n) {
                ans.push_back(now);
                return;
            }
            if (index > n || index == 10 || k <= 0) {
                return;
            }
            // not choose
            dfs(k, n, index + 1);
            // choose
            now.push_back(index);
            dfs(k - 1, n - index, index + 1);
            now.pop_back();
        }
    public:
        vector<vector<int>> combinationSum3(int k, int n) {
            dfs(k, n, 1);
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    小数据情况下,方法一的实际执行效果也许会优于方法二

    Python
    # from typing import List
    
    class Solution:
        def dfs(self, k: int, n: int, index: int) -> None:
            if not k and not n:
                self.ans.append(self.now[:])
                return
            if index > n or index == 10 or k <= 0:
                return
            self.dfs(k, n, index + 1)
            self.now.append(index)
            self.dfs(k - 1, n - index, index + 1)
            self.now.pop()
        
        def combinationSum3(self, k: int, n: int) -> List[List[int]]:
            self.ans = []
            self.now = []
            self.dfs(k, n, 1)
            return self.ans
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
    Tisfy:https://letmefly.blog.csdn.net/article/details/138033273

  • 相关阅读:
    脱离CRUD苦海 !性能优化全栈小册来了!
    vue子组件向父组件传参的方式
    动手吧,vue数字动画
    HOOPS Communicator对3D大模型轻量化加载与渲染的4种解决方案
    Redis分布式锁入门
    学习笔记——七周成为数据分析师《第二周:业务》:业务分析指标
    LeetCode二进制加法
    linux多线程编程之pthread_join和pthread_once使用
    美团一面:为什么线程崩溃崩溃不会导致 JVM 崩溃
    基于Python的校园学生一卡通管理系统
  • 原文地址:https://blog.csdn.net/Tisfy/article/details/138033273