下面是一个详细的解决方案。对于给定的数据,它会生成:
Data (6): 3 2 4 1 1 9
Found: 3 + 2 = 5
Found: 3 + 1 + 1 = 5
Found: 4 + 1 = 5
Found: 4 + 1 = 5
关键是要了解需要哪些数据来解决问题。你需要知道:
- 目标总
- 数量和未选中的元素列表(迄今为止的元素之和)
- 列表
的数量和已元素列表
'到目前为止'可以从列表中已经存在的元素的数量和列表中派生出来,但是传递它也很方便。这给出了一个6参数递归函数。它被调用期望的目标,到目前为止的总和为0,要搜索的元素的完整数组,以及足够大的空列表以容纳要搜索的所有元素。这发生在main()
。该函数标识是否找到任何添加到目标的列表(如果不是,则返回0,如果是,则返回1),因此测试和报告以及main()
的结束。
在该功能中,检查各种不可能的条件并进行救援。如果在顶部使用debug = 1
进行编译,则打印该函数的入口数据。扫描剩余的未经检查的元素,检测元素何时使得总和等于目标并报告这样做的列表(注意,它丢失了原始索引信息;可以添加该元素,但它是读者的练习)。如果元素足够小以便为另一个元素留出空间,请将该元素添加到找到的列表中,并将该数组的其余部分传递给该函数的递归调用。其他功能是简单的打印循环。
当目标被设定为40(而不是5),它可以正确地报告没有亚群被发现将高达40
有可能是更容易的方式编写它,但我病理反对不必要的全局变量(我认为debug
是一种有条件地编译代码而不是作为全局变量的方式)。可以将参数列表打包在一个结构中;不过,我不确定这会有多大帮助。如果我包括<stdbool.h>
,我可以使用bool
而不是int
(和false
和true
而不是0
和1
)。该代码是无悔的C99或C11;它不会像在C89下编写的那样编译(但是它不会很难与C89一起使用)。
代码:
#include <stdio.h>
static const int debug = 0;
static void found_one(int num, int *list)
{
printf("Found: ");
char const *pad = "";
int sum = 0;
for (int i = 0; i < num; i++)
{
printf("%s%d", pad, list[i]);
pad = " + ";
sum += list[i];
}
printf(" = %d\n", sum);
}
static void print_array(char const *tag, int num, int *arr)
{
printf("%s (%d):", tag, num);
for (int i = 0; i < num; i++)
printf(" %d", arr[i]);
putchar('\n');
}
static int find_subsets(int target, int sumsofar, int num_src, int *src,
int num_dst, int *dst)
{
if (sumsofar >= target)
return 0;
if (num_src <= 0)
return 0;
if (debug)
{
printf("-->> %s: sumsofar = %d, num_src = %d, num_dst = %d\n",
__func__, sumsofar, num_src, num_dst);
print_array("Source", num_src, src);
print_array("Dest'n", num_dst, dst);
}
int rc = 0;
int max = target - sumsofar;
for (int i = 0; i < num_src; i++)
{
if (src[i] == max)
{
dst[num_dst] = src[i];
found_one(num_dst + 1, dst);
rc = 1;
}
else if (src[i] < max)
{
dst[num_dst] = src[i];
if (find_subsets(target, sumsofar + src[i], num_src - (i + 1),
src + (i + 1), num_dst + 1, dst))
rc = 1;
}
}
if (debug)
printf("<<-- %s: %d\n", __func__, rc);
return rc;
}
int main(void)
{
int data[] = { 3, 2, 4, 1, 1, 9 };
enum { NUM_DATA = sizeof(data)/sizeof(data[0]) };
int subset[NUM_DATA];
int target = 5;
print_array("Data", NUM_DATA, data);
if (find_subsets(target, 0, NUM_DATA, data, 0, subset) == 0)
printf("Did not find any subsets totalling %d\n", target);
return 0;
}