回溯法:组合

2021-12-09 每日一题 LeetCode

# 0039. 组合总和 (opens new window)

题目

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

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

解析

依据每个位置的元素可选取的次数进行搜索。

代码

class Solution {
    vector<vector<int>> ans;
    vector<int> combine;
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        dfs(candidates, target, 0);
        return ans;
    }

    void dfs(vector<int>& candidates, int target, int idx) {
        if (target == 0) {
            ans.push_back(combine);
            return;
        }
        if (idx == candidates.size()) return;

        dfs(candidates, target, idx + 1);

        if (target >= candidates[idx]) {
            combine.push_back(candidates[idx]);
            dfs(candidates, target - candidates[idx], idx);
            combine.pop_back();
        }
    }
};
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

# 0040. 组合总和 II (opens new window)

题目

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

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

注意:解集不能包含重复的组合。

解析

本题与 0039. 组合总和 的区别在于需要对结果去重,同时每个元素只能选取一次。

可以先将原数组排序,在搜索时通过判断重复元素的个数,那么对元素 c[i] 最多可以选个 k[i] 次,从而转换成上题的解法。

代码

class Solution {
    vector<vector<int>> ans;
    vector<int> combine;
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        dfs(candidates, target, 0);
        return ans;
    }

    void dfs(vector<int>& candidates, int target, int idx) {
        if (target == 0) {
            ans.push_back(combine);
            return;
        }
        if (idx == candidates.size()) return;

        // 统计重复元素的个数
        int cnt = 1;
        while (idx + 1 < candidates.size() && candidates[idx] == candidates[idx + 1]) {
            idx++, cnt++;
        }

        // 尝试重复选取当前元素
        for (int i = 0; i <= cnt && target >= i * candidates[idx]; i++) {
            dfs(candidates, target - i * candidates[idx], idx + 1);
            combine.push_back(candidates[idx]);
        }

        // 恢复现场
        for (int i = 0; i <= cnt && target >= i * candidates[idx]; i++) {
            combine.pop_back();
        }
    }
};
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
28
29
30
31
32
33
34
35

# 0077. 组合 (opens new window)

题目

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

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

解析

枚举每个位置可能出现的数字,由于要求的是组合数,不考虑元素顺序,因此还需要记录一个 start 参数,表示当前数字从几开始选择。

代码

class Solution {
    vector<vector<int>> ans;
    vector<int> cur;
public:
    vector<vector<int>> combine(int n, int k) {
        dfs(1, n, k);
        return ans;
    }

    void dfs(int start, int n, int k) {
        if (k == 0) {
            ans.push_back(cur);
            return;
        }
        for (int i = start; i <= n; i++) {
            cur.push_back(i);
            dfs(i + 1, n, k - 1);
            cur.pop_back();
        }
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 0216. 组合总和 III (opens new window)

题目

解析

代码

# 0254. 因子的组合 (opens new window)

题目

解析

代码

# 0377. 组合总和 Ⅳ (opens new window)

题目

解析

代码

Last Updated: 2023-01-28 4:31:25