一道算法题,求更好的解法
问题(阿里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。请设计一个高效算法实现这个需求。
下面,给出一种解法:
代码
#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相关),而且最大的问题在于,无法去掉重复的组合。求更好的解法。