这是一个精心设计的解决方案。对于给定的数据,它会产生:
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;
}