/*
* problem precisely description:
* input: given two integer arguments: n and m
* output: all the subsets of array 1,2,...,n with sum of m
*/
解题思路:
显而易见, 当n>m时, 肯定存在满足题目要求的子集. 由于数列1,2,...,n是正整数数列, 这使得所有的大于m的数加上数列最小元素1都会大于m, 所以当n>m时我们只需要考虑子数列1,2,...,m即可.
当n<=m时,我们需要搜索1,2,...,n中符合条件的子集. 实际上, 我们可以将原问题分解成两个子问题:
子问题一: 在n个数的数列,我们取n, 然后原问题就变为找出1,2,...,n-1中和为m-n的所有子集,然后将所有的子集都加上元素n就得到了原问题的部分解集;
子问题二: 我们不取n, 原问题就变为找出1,2,...,n-1中和为m的所有子集.
联合两个子问题的解, 便得到了原问题的解集.
我们用subsets( n, m) 表示原问题, 那么原问题可表示为子问题 subsets(n-1, m) 和subsets( n-1, m-n)的解集的并集.
即: subsets( n, m) = subsets(n-1, m) U subsets( n-1, m-n)
实际中,可采用回溯的方法进行求解. 后面附有完整的代码.
实际的搜索方法类似于二叉树的方法, 去除不符合条件的解. 例如下面的二叉树就表示了1,2,...,n的全组合的求解过程. 左边分支表示取,右边的分支表示舍. 针对于0-1背包问题, 八皇后问题, 射击十次命中九十环的打法问题等等, 则需要剪除掉一些不符合条件的子树.
附解题代码:
/*
* problem description:
* Input two integers n and m, output all the subset of array 1,2,3,...,n with the
* sum of m.
*
* shaw 2011/12/17
*/
/*
* solutions:
* problem precisely description:
* input: given two integer arguments: n and m
* output: all the subsets of array 1,2,...,n with sum of m
*
* thought:
* Apparently, if n>m, some( one or more ) subsets are must existed here. And
* cause the array are all positive integers, so we don't need to consider (m,n].
* If n<m, then we can first compare the sum of array 1,2,...,n with m, if sum is
* less than m, then there is no subset fulfills the conditions, or if sum is equal
* to or larger than m, there are one or more subsets fulfills the request.
* Here we given the solutions:
* We start from the array of 1,2,..., min(n,m), if we take i=min(n,m) as
* one of the element of subset, the next we'll consider the following formulation:
* | == 0, now we have one subset, then we also have to backtrace
* | to get all those subset of array 1,2,...,i-1 with sum of i.
* |
* m-i |
* | <> 0, we first take i as one of the subset element, then
* | we have a sub-problem with the same request of the
* | original problem. We need to find a all subsets of array
* | 1,2,...,i-1 with sum of m-i
*
* in the searching process, we can use a stack to store the subset
*/
#include <stdio.h>
#include <assert.h>
#include <time.h>
#define MAXN 1000
/*stack for storing the subsets*/
int stack[MAXN];
int top = -1;
void push( int e )
{
assert( top < MAXN );
stack[++top] = e;
}
void pop( )
{
assert( top > -1 );
top--;
}
void clear()
{
top = -1;
}
void printStack( )
{
int i=top;
while( i>-1 )
printf("%d\t", stack[i--] );
printf("\n");
}
/*
* print all the subsets of array 1,2,...,n with sum of m
* constraints: m is less than 1000
*/
int subsets( int n, int m )
{
int i;
int num=0;
int flag = 1;
/* set n = min(m,n), i.e. if n>m just ignore the (m,n] */
if( n > m )
n = m;
/* we search from min(n,m) to 1*/
for( i = n; flag && i>=1; i-- )
{
/*take i */
push( i );
/* m-i == 0 */
if( m-i == 0 )
{
num++;
printStack();
}
else
{
/*if sum of array 1,2,...,i-1 is less than m-i, then start
searching with n-1*/
/* cut branch */
if( (i-1)*i/2 < (m-i) )
{
pop();
flag = 0;
continue;
}
/*search for subsets of array 1,2,...,i-1 with sum of m-i*/
num += subsets( i-1, m-i );
}
/*back trace*/
pop();
}
return num;
}
void testSubsets()
{
assert( 1 == subsets( 1, 1 ) );
printf( "\n\n" );
assert( 0 == subsets( 1, 2 ) );
printf( "\n\n" );
assert( 3 == subsets( 5, 6 ) );
printf( "\n\n" );
assert( 5 == subsets( 7, 7 ) );
}
int main()
{
testSubsets( );
clock_t start = clock();
subsets( 50, 50 );
clock_t duration = clock() - start;
printf("it takes %d seconds\n", duration/CLOCKS_PER_SEC );
return 0;
}