具有相等和的子集

时间:2013-10-21 16:10:50

标签: algorithm subset-sum

我想计算一组S的不相交子集S1和S2(S1 U S2可能不是S)存在多少对,其中S1中的元素之和= S2中元素的总和。

假设我已经计算了所有可能的2 ^ n子集的所有子集和。 我如何找到有多少不相交的子集具有相等的总和。

对于和值A,我们可以使用总和为A / 2的子集计数来解决这个问题吗?

举个例子: S = {1,2,3,4}

各种S1和S2设置可能是:

S1 = {1,2}且S2 = {3}

S1 = {1,3}且S2 = {4}

S1 = {1,4} nd S2 = {2,3}

以下是问题的链接: http://www.usaco.org/index.php?page=viewproblem2&cpid=139

2 个答案:

答案 0 :(得分:1)

[编辑:修复了愚蠢的复杂性错误。谢谢kash!]

实际上我相信你需要使用 O(3 ^ n)算法described here来回答这个问题 - O(2 ^ n)分区算法只是好的足以枚举所有不相交的子集,其联合是整个地面集

正如我所链接的答案所述,对于每个元素,您基本上决定是否:

  1. 把它放在第一组,
  2. 将其放入第二组,或
  3. 忽略它。
  4. 考虑到每种可能的方法都会生成一个树,其中每个顶点有3个子节点:因此O(3 ^ n)时间。需要注意的一点是,如果您生成一个解决方案(S1,S2),那么您也不应该计算解决方案(S2,S1):这可以通过在构建它们时始终保持两组之间的不对称来实现,例如:强制S1中的最小元素必须始终小于S2中的最小元素。 (这种不对称执行具有将执行时间减半的良好副作用:))


    特殊(但可能在实践中常见)案例的加速

    如果您希望集合中有许多小数字,则可以使用另一种可能的加速:首先,按递增顺序对列表中的所有数字进行排序。选择一些最大值m,越大越好,但足够小,你可以买得起一个m大小的整数数组。我们现在将数字列表分成两部分,我们将分别处理:最初总和为m的数字的初始列表(此列表可能非常小),其余部分。假设第一个k <= n个数字适合第一个列表,并调用该第一个列表Sk。原始列表的其余部分我们将称为S'。

    首先,将整数的size-m数组d[]初始化为全0,并像往常一样解决Sk的问题 - 但不是仅记录具有相等和的不相交子集的数量,而是增加{{1对于每个对的不相交子集Sk1和Sk2,由这些前k个数组成。 (同时增加d[abs(|Sk1| - |Sk2|)]以计算Sk1 = Sk2 = d[0]时的情况。)想法是在第一阶段结束后,{}将记录2个不相交子集的方式数可以从S的前k个元素生成具有d[i]的差异。

    其次,像往常一样处理余数(S') - 但不是仅记录具有相等总和的不相交子集的数量,而是每当i时,将|S1'| - |S2'| <= m添加到解决方案的总数中。这是因为我们知道有很多种方法可以从具有这种差异的前k个元素构建一对不相交的子集 - 对于这些子集对d[abs(|S1'| - |S2'|)]中的每一个,我们可以添加较小的Sk1或Sk2 S1'或S2'中的较大者,另一个中的另一个,用一对具有相等和的不相交子集结束。

答案 1 :(得分:0)

这是一个clojure解决方案。

它将s定义为1,2,3,4的集合 然后将所有子集定义为所有大小为1 - 3

的集合的列表

一旦定义了所有子集,它就会查看所有子集对,并仅选择不相等的对,不与原始集合并且其总和相等

(require 'clojure.set)
(use 'clojure.math.combinatorics)

(def s #{1, 2, 3, 4})
(def subsets (mapcat #(combinations s %) (take 3 (iterate inc 1))))

(for [x all-subsets y all-subsets 
          :when (and (= (reduce + x) (reduce + y)) 
                     (not= s (clojure.set/union (set x) (set y))) 
                     (not= x y))] 
      [x y])

产生以下内容:

([(3) (1 2)] [(4) (1 3)] [(1 2) (3)] [(1 3) (4)])