题目:
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
- All numbers (including target) will be positive integers.
- Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
- The solution set must not contain duplicate combinations.
For example, given candidate set 2,3,6,7
and target 7
,
A solution set is:
[7]
[2, 2, 3]
题目大意:题目给定一组数(无序)以及一个数字X,要求输出由该组数组合成X的所有组合,且按照升序排列,允许重复使用数字。
解题思路:之前做了一道N皇后的题目,看到别人采用回溯算法,思路比较清楚,而这道题也恰好可以使用回溯算法。
回溯算法的主要难点在于寻找回溯停止条件,回溯策略以及约束条件。对于这道题目:
约束条件:target 是否等于找到数组合的和。
回溯策略:首先对给定的一组数按升序排序,假设有个棋盘格第一行放置该组数,第二行也放置该组数...这就是要搜索的解空间。以[2,3,4]为例
2 3 4
2 3 4
2 3 4
那么最多有target / combination[0] + 1 行。接下来的策略就是,当target < total时,向下搜索,当target == total时,输出结果,并且向上回溯一行(因为是升序排列),当target > total时,向上回溯一行,需要注意当搜索的列大于combination.size()时,也需要回溯一行。
回溯停止条件:当row == -1 时,算法结束。代码:
class Solution {
public:
// Judge whether the sum is equal to target
int isEqual(vector<int> &candidates, vector<int> choosen_num, int row, int target){
int total = 0;
for(int i = 0; i <= row; ++i)
total += candidates[choosen_num[i]];
if(total == target)
return 0;
else if(total < target)
return 1;
else
return -1;
}
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
// Sort the candidates, at first.
sort(candidates.begin(), candidates.end());
vector<int> choosen_num(target / candidates[0] + 1, 0);
int row = 0;
int col_max = candidates.size();
vector<vector<int> > result;
while(1){
// Backtracking
// Cols must smaller than candidates's max cols.
if(choosen_num[row] == col_max){
row--;
if(row == -1)
break;
choosen_num[row]++;
}else{
int flag = isEqual(candidates, choosen_num, row, target);
if(flag == 1){
choosen_num[row+1] = choosen_num[row];
row++;
}else if( flag == 0){
vector<int> temp_result;
for(int i = 0; i <= row; ++i)
temp_result.push_back(candidates[choosen_num[i]]);
result.push_back(temp_result);
row--;
if(row == -1)
break;
choosen_num[row]++;
} else{
row--;
if(row == -1)
break;
choosen_num[row]++;
}
}
}
return result;
}
};
加强版:
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:
- All numbers (including target) will be positive integers.
- Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
- The solution set must not contain duplicate combinations.
For example, given candidate set 10,1,2,7,6,1,5
and target 8
,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]
针对Combination Sum2的版本只需要将每行的首次偏移置为上一行当前值+1既可避免元素重复,为了避免重复解,使用set<vector<int>>即可,最后稍作转换即可,代码如下:
class Solution {
public:
// Judge whether the sum is equal to target
int isEqual(vector<int> &candidates, vector<int> choosen_num, int row, int target){
int total = 0;
for(int i = 0; i <= row; ++i)
total += candidates[choosen_num[i]];
if(total == target)
return 0;
else if(total < target)
return 1;
else
return -1;
}
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
// Sort the candidates, at first.
sort(candidates.begin(), candidates.end());
vector<int> choosen_num(target / candidates[0] + 1, 0);
int row = 0;
int col_max = candidates.size();
set<vector<int> > result;
while(1){
// Backtracking
// Cols must smaller than candidates's max cols.
if(choosen_num[row] == col_max){
row--;
if(row == -1)
break;
choosen_num[row]++;
}else{
int flag = isEqual(candidates, choosen_num, row, target);
if(flag == 1){
choosen_num[row+1] = choosen_num[row] + 1;
row++;
}else if( flag == 0){
vector<int> temp_result;
for(int i = 0; i <= row; ++i)
temp_result.push_back(candidates[choosen_num[i]]);
result.insert(temp_result);
row--;
if(row == -1)
break;
choosen_num[row]++;
} else{
row--;
if(row == -1)
break;
choosen_num[row]++;
}
}
}
return vector<vector<int>>(result.begin(), result.end());
}
};