目录
前言:
77.组合
题目描述:
输入输出示例:
思路和想法:
216. 组合总和 III
题目描述:
输入输出示例:
思路和想法:
17. 电话号码的字母组合
题目描述:
输入输出描述:
思路和想法:
40. 组合总和 II
题目描述:
输入输出描述:
思路和想法:
这里对于组合问题,进行了深入的探讨(ACM模式):
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例1:
输入:n = 4,k = 2 输出:[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
示例2:
输入:n = 1,k = 1 输出:[[1]]
提示:
回溯法的问题,都可以抽象为树形结构问题。
回溯法解决的是在集合中递归查找子集,集合的大小构成树的宽度,递归的深度构成树的深度。
这道题目属于组合问题的模板题。
#include
using namespace std;
/*
* 作者:希希雾里
* 77.组合
* */
/*
* 这里我们能够比较清晰的,要使用回溯。
* */
vector> result;
vector path;
//这里对于回溯要传入的参数:元素数量,限定条件以及要开始遍历的元素
//这里能够比较清晰的知道n即为回溯的宽度,k即为回溯的深度。
void backtracing(int n, int k, int startIndex){
/*终止条件*/
if(path.size() == k){
result.push_back(path);
return;
}
/*处理节点 + 回溯撤销*/
for (int i = startIndex; i <= n; ++i) {
path.push_back(i);
backtracing(n, k, i+1);
path.pop_back();
}
};
int main() {
int n, k;
cin>> n >> k;
result.clear();
path.clear();
backtracing(n, k,1);
//结果输出
for (int i = 0; i < result.size(); ++i) {
for (int j = 0; j < result[i].size(); ++j) {
cout << result[i][j];
if(j != result[i].size() - 1) cout << " ";
}
//注意最后输出不需要换行。
if(i == result.size() - 1) return 0;
cout << endl;
}
return 0;
}
/* 测试样例
4 2
1 1
*
* */
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例1:
输入:k = 3, n = 7 输出:[[1,2,4]]
示例2:
输入:k = 3, n = 9 输出:[[1,2,6], [1,3,5], [2,3,4]]
示例3:
输入: k = 4, n = 1 输出:[]
提示:
这道题目和上一道题基本一致,在原有基础上,还需要统计path里元素的总和,终止条件发生了改变, path.size() == k && sum == n 。
#include
using namespace std;
/*
* 作者:希希雾里
* 216.组合总和III
* */
vector> result;
vector path;
int sum = 0;
//这里对于回溯要传入的参数,集合个数,目标总和以及要遍历的元素下标
void backtracing(int k, int n, int startIndex){
/*终止条件*/
if(path.size() == k && sum == n){
result.push_back(path);
return;
}
/*处理节点 + 回溯撤销*/
for (int i = startIndex; i <= 9; ++i) {
path.push_back(i);
sum += i;
backtracing(n, k, i + 1);
sum -= path.back();
path.pop_back();
}
};
int main() {
int n, k;
cin>> n >> k;
result.clear();
path.clear();
backtracing(k, n,1);
//结果输出
for (int i = 0; i < result.size(); ++i) {
for (int j = 0; j < result[i].size(); ++j) {
cout << result[i][j];
if(j != result[i].size() - 1) cout << " ";
}
//注意最后输出不需要换行。
if(i == result.size() - 1) return 0;
cout << endl;
}
return 0;
}
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例1:
输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例2:
输入:digits = "" 输出:[]
示例3:
输入:digits = "2" 输出:["a","b","c"]
提示:
这里首先构建数字和字符之间的映射,后续就是组合问题,采用回溯方法就可以解决了。
#include
using namespace std;
/*
* 作者:希希雾里
* 17.电话号码的字母组合
* */
vector result;
string s;
//构建map,建立映射
const string map_letter[10] = {
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz",
};
/*
* 函数: backtracing()
* 输入参数: digits:输入的字符串 index:字符串元素下标
* */
void backtracing(const string & digits, int index){
/*终止条件*/
if(index == digits.length()){
result.push_back(s);
return;
}
int digit = digits[index] - '0';
string letters = map_letter[digit];
/*处理节点 + 回溯过程*/
for (int i = 0; i < letters.length(); ++i) {
s.push_back(letters[i]);
backtracing(digits, index + 1);
s.pop_back();
}
};
int main() {
result.clear();
s.clear();
//字符串输入
string str;
getline(cin,str);
backtracing(str, 0);
//结果输出
for (int i = 0; i < result.size(); ++i) {
//注意最后输出不需要换行。
cout << result[i];
if(i == result.size() - 1) return 0;
cout << endl;
}
return 0;
}
/* 测试样例
23
2
*
* */
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例1:
输入:candidates = [10,1,2,7,6,1,5], target = 8 输出:[ [1,1,6], [1,2,5], [1,7], [2,6] ]
示例2:
输入:candidates = [2,5,2,1,2], target = 5, 输出:[ [1,2,2], [5] ]
提示:
这道题目和之前不一样的地方在于,要取的数组里有数值相等的元素并且要求解集不能包含重复的组合。所以这道题目涉及到了去重。
一个组合里可以有重复的元素,即不需要树枝去重(纵向)。不能有重复的组合,即要进行树层去重(横向)。
树层去重和树枝去重,是根据树形结构,提出的去重概念。
那么转换到具体实现上,需要解决两个问题:
#include
using namespace std;
/*
* 作者:希希雾里
* 40. 组合总和 II
* */
vector> result;
vector path;
int sum = 0;
/*
* 函数: backtracing()
* 输入参数: candidates:输入的数组 target: 目标数值 index:字符串元素下标 used:标志数组
* */
void backtracing(vector& candidates, int target, int index, vector& used){
if(sum == target){
result.push_back(path);
return;
}
for(int i = index; i < candidates.size();++i){
if(i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false){
continue;
}
path.push_back(candidates[i]);
sum += candidates[i];
used[i] = true;
backtracing(candidates, target, i + 1, used);
used[i] = false;
sum -= candidates[i];
path.pop_back();
}
};
int main() {
result.clear();
path.clear();
vector candidates;
int n;
while(cin >> n){
candidates.push_back(n);
if(getchar() == '\n'){
break;
}
}
int target;
cin >> target;
vector used(candidates.size(),false);
//默认升序排序
sort(candidates.begin(),candidates.end());
backtracing(candidates, target, 0, used);
//结果输出
for (int i = 0; i < result.size(); ++i) {
for (int j = 0; j < result[i].size(); ++j) {
cout << result[i][j];
if(j != result[i].size() - 1) cout << " ";
}
//注意最后输出不需要换行。
if(i == result.size() - 1) return 0;
cout << endl;
}
return 0;
}
/* 测试样例
10 1 2 7 6 1 5
8
2 5 2 1 2
5
*
* */