问题描述
所以最近我一直在尝试解决一些有趣的问题,但是在这个问题上却陷入了困境。
给出一组对象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)