如何减少子集和问题的时间复杂度

问题描述

所以最近我一直在尝试解决一些有趣的问题,但是在这个问题上却陷入了困境。

给出一组对象1到N。n1个对象的值为0,n2个对象的值为1,n3个对象的值为2。 需要计算所有总和为3或其倍数的子集。

n1 + n2 + n3 = N

我已经能够解决问题,但无法降低该问题的时间复杂度。 我的解决方案仍然具有指数级的时间复杂度。我试图降低时间复杂度,以便可以在5秒内运行N = 100,000的大型设备。

这是我所迷惑的另一个简单但直观的解决方案,但问题又是时间的复杂性。

def mask(lst,m):
    # pad number to create a valid selection mask
    # according to deFinition in the solution laid out
    m = m.zfill(len(lst))
    return map(lambda x: x[0],filter(lambda x: x[1] != '0',zip(lst,m)))

def subset_sum(lst,target):
    # there are 2^n binary numbers with length of the original list
    for i in range(2**len(last)):
        # create the pick corresponsing to current number
        pick = mask(lst,bin(i)[2:])
        if sum(pick) == target:
            yield pick

我不需要确切的编码解决方案,但是如果您能向我指出正确的方向并且任何编程语言都可以,那么我会很好,我想知道该解决方案的逻辑/方法

解决方法

似乎给定一个数字数组(所有数字均为0、1或2),您想要返回这些数字的每个子集,它们的总和为3的倍数,并且您想将其多项式地进行。

这是不可能的。

要查看此信息,请考虑数组[2,1,0,...,0]。要获得3的总和,我们总是需要前两个数字,但除此之外,任何子集的总和为3。由于我们有N-2 0,所以总共有2**(N-2)个适合的子集要求。返回多项式的答案不能在多项式时间内完成。

,

所以这里的关键知识是和必须为3的倍数,即sum = 0(mod 3)。

这意味着您可以在任意子集中包含任意数量的n1,因为它们不会改变总和。

因此,您可以立即减少问题,从n2和n3中找到总和为0(模3)的子集,然后将所有可能的2 ** n1个子集相加。请注意,对于n1的任何显着值(例如大于50),将其列出都变得毫无争议。

但是它很容易计数:

那么如何从n2和n3中找到总和= 0(模3)的集合。我们知道n2中的任何元素都会使总和加1,因此n2中有m个元素,我们可以计算出其中可能有多少个元素

如此宽松的伪代码用于计数:

def f(n1,n2,n3):
    for i in range(0,n2+1):
        # i elements of n2
        # compute i%3
        # if i%3 0 then we can pick j = 0,3,6,9 ... elements from n3
        # if i%3 1 then we can pick j = 1,4,7,10 .. elements from n3
        # if i#3 2 then we cannot j = 2,5,8,11 .. elements from n3
        # compute the combination c = (n choose i) * (n choose j) for each j
        # for each c multiply by 2**n1 (adding on all possible subsets from n1)