问题(阿里2010年实习):
给定一个数t,以及n个整数,在这n个整数中找到相加之和为t的所有组合,例如t = 4,n = 6,这6个数为[4, 3, 2, 2, 1, 1],这样输出就有4个不同的组合,它们的相加之和为4:4, 3+1, 2+2, and 2+1+1。请设计一个高效算法实现这个需求。
下面,给出一种解法:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include
<
stdio.h
>
#define LEN 20
int path[LEN];
int arr[LEN] = { 1 , 1 , 1 , 2 , 2 , 3 , 4 , 4 , 5 , 5 , 5 , 8 , 8 , 8 , 10 , 10 , 10 , 12 , 12 , 12 };
void clearpath()
{
int i;
for (i = 0 ; i < LEN; i ++ )
{
path[i] = 0 ;
}
}
void out_put()
{
int i;
for (i = LEN - 1 ; i >= 0 ; i -- )
{
if (path[i] == 1 )
printf( " %d " , arr[i]);
}
printf( " \n " );
}
/* 算法:
**q(n, t)表示从arr[0]...arr[n]中选出和为t的子集,则
**q(n, t) = q(n-1, t-arr[n]) + q(n-1, t)
*/
void q( int n, int t, int flag)
{
if (t < 0 )
return ;
path[n + 1 ] = flag;
if (n == 0 )
{
if (t == 0 )
{
path[n] = 0 ; // 不包含arr[0]
out_put();
}
else if (arr[n] == t)
{
path[n] = 1 ; // 包含arr[0]
out_put();
}
}
else
{
q(n - 1 , t - arr[n], 1 ); // 包含arr[n]
q(n - 1 , t, 0 ); // 不包含arr[n]
}
}
void subsum( int len, int t)
{
q(len - 1 , t - arr[len], 1 );
q(len - 1 , t, 0 );
}
int main()
{
int t = 20 ;
clearpath();
subsum(LEN - 1 , t);
return 0 ;
}
#define LEN 20
int path[LEN];
int arr[LEN] = { 1 , 1 , 1 , 2 , 2 , 3 , 4 , 4 , 5 , 5 , 5 , 8 , 8 , 8 , 10 , 10 , 10 , 12 , 12 , 12 };
void clearpath()
{
int i;
for (i = 0 ; i < LEN; i ++ )
{
path[i] = 0 ;
}
}
void out_put()
{
int i;
for (i = LEN - 1 ; i >= 0 ; i -- )
{
if (path[i] == 1 )
printf( " %d " , arr[i]);
}
printf( " \n " );
}
/* 算法:
**q(n, t)表示从arr[0]...arr[n]中选出和为t的子集,则
**q(n, t) = q(n-1, t-arr[n]) + q(n-1, t)
*/
void q( int n, int t, int flag)
{
if (t < 0 )
return ;
path[n + 1 ] = flag;
if (n == 0 )
{
if (t == 0 )
{
path[n] = 0 ; // 不包含arr[0]
out_put();
}
else if (arr[n] == t)
{
path[n] = 1 ; // 包含arr[0]
out_put();
}
}
else
{
q(n - 1 , t - arr[n], 1 ); // 包含arr[n]
q(n - 1 , t, 0 ); // 不包含arr[n]
}
}
void subsum( int len, int t)
{
q(len - 1 , t - arr[len], 1 );
q(len - 1 , t, 0 );
}
int main()
{
int t = 20 ;
clearpath();
subsum(LEN - 1 , t);
return 0 ;
}
该算法的时间复杂度为指数级(但实际情况应该好很多,与t相关),而且最大的问题在于,无法去掉重复的组合。求更好的解法。