使用约束拆分子集

时间:2018-01-11 05:19:03

标签: algorithm subset-sum

今天,在练习一些算法问题时,我发现了一个有趣的问题。 问题是

  

你必须将1除以Form(一个缺失值n)为两等   这两半的总和相等的一半。

     

示例:

     

如果xn = 7

     

解决方案将是x = 4{7, 5}

我可以用蛮力方法回答它,但我想要一个有效的解决方案 任何人都可以帮助我吗?

2 个答案:

答案 0 :(得分:0)

如果没有x的元素1→N的总和是奇数,那么就没有解。

否则,您可以在O(N)中找到平衡选择的解决方案。

连续4次

首先让我们考虑四个连续数字的任何序列可以分成两组,并给出相等的总和:

  

[x,x + 1,x + 2,x + 3]→[x + 3,x]; [x + 2,x + 1]

因此选择它们并将它们放入集合中A B B A平衡集合A和B.

4

此外,当我们在省略的值上有两对夫妻时,它可以拥有类似的属性:

  

[x-2,x-1,x + 1,x + 2]→[x + 2,x-2]; [x + 1,x-1]

所以仍然是A B B A

此时我们可以解决以下问题:

  • 我们有一个四元组:我们将其拆分为案例1
  • 我们有2个数字,x和其他2个数字:我们按案例2分割

好吧,但可能会发生我们有3个数字,x和其他3个数字或其他条件。我们怎么能以平衡的方式选择呢?

+2 Gap

如果我们再看一下x:

之间的差距
  

[x-1,x + 1]

我们可以注意到,如果我们将两个邻居分成两个独立的集合,我们必须平衡集合上的+2和更大的总和。

平衡尾巴

我们可以使用序列的最后四个数字来完成此操作:

  

[4 3 2 1]→[4,2]; [3,1]→6; 4

最后我们必须考虑到我们可能没有其中一个,所以让我们构建另一个案例:

  

[3 2 1]→[2]; [3,1]→2; 4

让我们也意识到我们可以在A B A B(或B A B A)模式的序列的另一端做同样的事情 - 如果我们的+2代表B(或A);

4跨+

令人惊讶的是,如果我们跳h(奇数!)数字仍然有4个:

  

[x + 3,x + 2,x-2,x-3]→[x + 3,x-3]; [x + 2,x-2]

因此,探索数组我们可以逐步绘制解决方案

一个例子:

11 10 9 8 7 6 5 4 3 2 1

它的总和是偶数,因此x只能是偶数:

x = 10
11 - 9 | 8 7 6 5 | 4 3 2 1 → (+2 gap - on A) (4 in a row) (balancing tail)
 A   B   A B B A   B A B A

x = 8
11 10 | 9 - 7 | 6 5 | 4 3 2 1 → (4 across +) (+2 gap - on A) (balancing tail)
 a  b   A   B | b a | B A B A

x = 6
11 10 9 8 | 7 - 5 | 4 3 2 1 → (4 in a row) (+2 gap - on A) (balancing tail)
 A  B B A   A   B   A B B B

x = 4 we have no balancing tail - we have to do that with head
11 10 9 8 | 7 6 | 5 - 3 | 2 1 → (balancing head) (4 across +) (+2 gap)
 A  B A B   A B | b   a | B A

x = 2
11 10 9 8 | 7 6 5 4 | 3 - 1 → (balancing head) (4 in a row) (+2 gap)
 A  B A B   A B B A   B   A

有趣的是注意到解决方案的对称性。另一个例子。

10 9 8 7 6 5 4 3 2 1

它是奇数的总和,所以x只能是一个奇数,现在元素的数量是奇数。

x = 9
10 - 8 | 7 6 5 4 | 3 2 1 → (+2 gap - on A) (4 in a row) (balancing tail)
 A   B   A B B A   B A B

x = 7
10 9 | 8 - 6 | 5 4 | 3 2 1 → (4 across +) (+2 gap - on A) (balancing tail)
 a b | A   B | b a   B A B

x = 5
10 9 8 7 | 6 - 4 | 3 2 1 → (4 in a row) (+2 gap - on A) (balancing tail)
 A B B A   A   B   B A B

x = 3
10 9 8 7 | 6 5 | 4 - 2 | 1 → (balancing head) (4 across + virtual 0) (+2 gap)
 A B A B   B A | a   b | A

x = 1
10 9 8 7 | 6 5 4 3 | 2 → (balancing head) (4 in a row) (+2 gap virtual 0)
 A B A B   A B B A   B 

最后值得注意的是,只要我们有一个完整的平衡段( 4连续或4对),我们就可以从A切换到B.

搞笑说 - 但要求总和([1 ... N] -x)的财产是平均的,如果你自己尝试就会使案件多余。

我很确定这个算法可以推广 - 我很快就会提供一个修订版本。

答案 1 :(得分:0)

通过使用预处理步骤包装标准subset sum problem动态编程,可以解决此问题。这些步骤是O(1)com

算法(n,x):

  1. sum = n *(n + 1)/ 2
  2. neededSum = sum - x
  3. if(neededSum%2!= 0):return 0

  4. 创建数组[1..n]并从中删除x

  5. 调用标准子集(arr,0,neededSum / 2,[])
  6. 子集算法的工作python实现 - 打印所有子集如下。

    def subsetsum(arr, i, sum, ss):
        if i >= len(arr):
            if sum == 0:
                print ss
                return 1
            else:
                return 0
    
        ss1 = ss[:]
        count = subsetsum(arr, i + 1, sum, ss1)
        ss1.append(arr[i])
        count += subsetsum(arr, i + 1, sum - arr[i], ss1)
        return count
    
    
    arr = [1, 2, 3, 10, 5, 7]
    sum = 14
    a = []
    print subsetsum(arr, 0, sum, a)
    

    希望它有所帮助!