The question is to find combination of numbers sum up to a target in a set.
Solution:
1. Note that a set contains no duplicates.
2. the combination could be unlimited in size. Say target = 3, array = [1] then the output is [1,1,1].
There are two troubles in this question:
1. how to find combination that sum up to a target.
2Sum and 3Sum would just consider any combination of 2 numbers and 3 numbers correspondingly. However in this problem, the combination is unlimited.
2. how to avoid duplicates.
We know that if target = 4 and array = [1,3]. If we do not try to prevent duplicates, it would probably gives results as {[1,3] , [3,1]}.
First Lets tackle the first trouble:
Since the combination is unlimited, recurrence should be a proper method. Let's look at the recurrent structure here.
We know that if a + b + c + ...+ z = target, then a + b +c + ....+y = target - z. That is, if a target could be reached, then target - one element in the combination could also bereached. Another example is target = 6, array = [1,2, 3, 4, 6]. one combination = 1 + 2 + 3 = 6 We could see that 6 - 1 = 5 and 5 could also be found since 5 = 2 + 3. So the idea is:
findSum(vector<int> sum_components, int target, vector<int> candidates, vector<int> *result){
<span style="white-space:pre"> </span>if(target == 0) put sum_components into result// since target has reached 0, we successfully reached the original target
<span style="white-space:pre"> </span>if(target < 0) return//the sum of sum component has exceeded the original target. We abandon this combination
<span style="white-space:pre"> </span>for each component in candidates
<span style="white-space:pre"> </span>add component into sum_components
<span style="white-space:pre"> </span>findSum(sum_components, target - component, candidate, result)//find if target - component could be summed up
<span style="white-space:pre"> </span>end for
}
We have to solve the second problem, that is, getting rid of duplicates.
The way to get rid of duplicates is to maintain an index indicating where we start the combination. Say array = [3,4], target = 7. The result would give [3,4] and [4,3]. To avoid this, when recurrence is dealing with 4, 4 should not sum up with any number at its left. We know that each number would have its recurrence scanning through its right side. Thus if a number sums to target with a number at its left, this combination would have been catched by its left number. In the example, 3 would sum up with 4 in its recurrence. When the recurrence starts from 4 see 4 + 3 = 7, the [3,4] combination has already been catched by the recurrence from 3.
Code:
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> members;
sort(candidates.begin(), candidates.end());//make it in order.This is necessary for having no duplicates in results
findSum(members,candidates,0, target, &res);
return res;
}
void findSum(vector<int> members, vector<int> candidates,int start_ind, int target, vector<vector<int>> *res){
if(target < 0) return;//no number can sum up to target
if(target == 0){//have already sum up to target
res->push_back(members);
return;
}
if(target < candidates[0]) return;//no number can sum up to target
vector<int> temp_mem;
for(int i = start_ind; i < candidates.size(); i ++){//start_ind is to avoid duplicate.
<span style="white-space:pre"> </span> //Each number try to combine with numerbs at its right side
if(candidates[i] > target) break;//number is larger than target
else{
temp_mem = members;
temp_mem.push_back(candidates[i]);//try this number.
findSum(temp_mem,candidates,i,target - candidates[i], res);//try if some number can sum up target - candidates[i].
}
}
}
};