如果该集合S = {1,...,N}被划分为两个总和相等的子集,则该总和必须是 的总和的一半S;的总和S是N(N+1)/2所以分区中每个子集的总和必须是N(N+1)/4。它也必须是整数,因此N(N+1)/2必须是偶数。
所以问题归结为找到总和为 的 S 的子集的数量N(N+1)/4。分区的总数正好是这个数字的一半,因为每个分区包含两个这样的子集,并且没有两个分区共享一个子集。
这应该是显而易见的。
现在,让我们列出任何和任何集合的S总和的子集。任何这样的子集都必须有一个最大值,它必须是 的一个元素。最大值必须是 的最大元素,或者必须小于 的最大元素。这两组子集是不同的,所以我们可以分别列举出来。我们称 为 的最大元素。kkSSSSS Smax
第二组很简单:它只是总和为的子集。我们可以通过递归调用子集列表器来找到它们。但是第一组几乎一样简单:组中的每个集合都包括,其其余元素是一些集合,其中加起来为,我们可以再次递归列出。为了完成递归,我们注意到 if是空集,那么 if ,恰好有一个合格子集(空集本身),如果 k 不为 0,则不存在合格子集。每次递归都会从 中删除一个元素,因此最终必须达到终止条件。S - { Smax }kSmaxS - { Smax }k - SmaxSk = 0S
应该清楚,S上述递归函数将使用的子集只是从1to的数字,因此我们可以完全摆脱,并将递归编写如下:SmaxS
Subsets(min, max, k) =
Subsets(min, max - 1, k)
⋃ { {max, P} | P ∈ Subsets(min, max - 1, k - max) }
但是我们只想要分区数的计数,所以我们可以稍微简化一下:
Count_Subsets(min, max, k) =
Count_Subsets(min, max - 1, k)
+ Count_Subsets(min, max - 1, k - max)
我们需要通过添加结束条件来完成递归:
If min > max, Count_Subsets(min, max, k) = 1 if k = 0; otherwise 0
(事实上,很容易证明递归意味着1当 k 递减到0并且0如果k小于时该值将是0,因此我们可以使终止条件更早发生。)
That gives us the simple recursion for the count. But since it calls itself twice, it would be better to work backwards ("dynamic programming"). We need to compute Count_Subsets(1, N, N*(N+1)/4), which will require us to compute values of Count_Subsets(1, max, k) for all values of max from 1 to N, and from all values of k from 0 to N*(N+1)/4. We do that by starting with max = 0, and working up until we reach min = N. And that's precisely what your algorithm does; i is max, and the array is the set of values for k from 0 to N(N+1)/4.
By the way, as should be apparent from the above description, a[j] should be incremented by a[j - i], not by a[j - 1]