- Example: candidate set [-1, 3, 2, 1, 5], target value 4. Then we can have 1+3, -1+2+3
Solution: Recursion is used to solve this problem. Also first sort the candidate set.
- Assume we have a prev_sum (the sum of the previous integers we had added up) and current integer we want to add, if prev_sum + current integer > target value, we return.
- if prev_sum + current integer = target value, we print out the combination and also return. No need to explore further, since the candidate set is sorted.
- if prev_sum + current integer < target value, we take the current integer, update prev_sum, and also update an array that is used to track the combination, then we make a recursive function call
- an array, int index[] is used to track the integers we had used. The advantage of this array is simplicity. Definitely we can use STL vectors.
- If the target value is negative, taking the above approach naively will have some problem (see chunjie's comment below). There are different ways to handle this case. One way used below is to convert the target value into positive and also convert the whole set of integers by multiplying with a "-1".
void print_comb(int index[], int n, vector<int> set, int negative)
{
for(int i =0; i<=n; i++)
cout << (negative * set[index[i]]) << ((i<n)?'+':' ');
cout << endl;
}
//assume a sorted list
void _print_all_combine_n(int target, int negative, int n_prev, int index[], vector<int> set, int pos, int n)
{
for(int i = pos; i < set.size(); i++)
{
if(n_prev + set[i] == target)
{
index[n] = i;
print_comb(index, n, set, negative);
return;
}
// we don't need to look at later cases, since they are not possible
if(n_prev + set[i] > target)
return;
// skip duplicate numbers before advancing, since we only use each number once
int m = i+1;
while(set[m] == set[i] && m < set.size())
m++;
i = m-1;
if(n_prev + set[i] < target)
{
index[n] = i;
print_all_combine_n(target, n_prev+set[i], index, set, i+1, n+1);
}
}
}
//assume a sorted list
void print_all_combine_n(int target)
{
int index[10000];
index[0] = 0;
vector<int> set(myints, myints + sizeof(myints) / sizeof(int) );
int negative = 1;
if(target < 0)
{
for(int i=0; i<set.size(); i++)
set[i] = (-1)*set[i];
negative = -1;
}
sort(set.begin(), set.end());
print_all_combine_n(target * negative, negative, 0, index, set, 0, 0);
}
int main()
{
int myints[] = {10, 1, 2, 7, 6, 1, 5, -3};
print_all_combine_n(8);
}
This comment has been removed by the author.
ReplyDeleteHi, input as {-3, -2, -1, 0, 1} and target = -5
ReplyDeleteyour program will exit right after "if(n_prev + set[i] > target)".
I have my own version of implementation but it seems little bit ugly.
The basic idea is the same
template
void PrintVector(vector &v){
for(int i = 0; i < v.size(); i++)
cout< &path, bool &bChanged){
if(sum == t){
if(path.size() > 0 && bChanged){//bChanged: whether it has been change since last output
PrintVector(path);
bChanged = false;
}
}
if(i == n) return;
int j = i;
while(j+1 < n && a[j] == a[j+1]) j++;
// cnt = j-i+1
for(int k = 0; k <= j-i+1; k++){
if(k != 0){
path.push_back(a[i]);
bChanged = true;// mark it as changed since we just insert a new element
}
get_all_comb(a, n, j+1, t, sum+k*a[i], path, bChanged);
}
for(int k = 1; k <= j-i+1; k++) path.pop_back();
}
Thanks so much for your comment. It is true that my original implementation failed to handle the case where the "target" is negative. I updated the implementation. Basic idea is to convert the target to a positive one.
Deletewhat about this one? {-3,-2,-1,0, 1, 1, 1}; target = 2
DeleteI just began to browse your website, it is quite helpful to me. After going all through this site. I might consider to look for a job in USA, hope to get some help if possible by then. haha~ BTW, i just found i know you : ) you are famous in our University , ZJU right?
It is nice that this website can be helpful to you. You definitely can try to apply for a job in US. Though I have some friends from ZJU, actually I am not from ZJU.
Deletemy bad, I thought you are Ma Xiao -___- haha
ReplyDeleteIt happens to be that I know him:)
Delete