几乎没有约束的背包问题变体
knapsack problem variation with almost no constraints
我有这种背包的变体,几乎没有限制,没有限制让我真的不知道从哪里开始。
给定一组正整数。可能:
1 2 3 4 5 6 7 8 9 10 11 12 13
找到两个总和相同的非重叠子集。这两个集合不需要包含 S 中的所有数字。
所以对于前一个例子,答案是
[1,2] 和 [3]
通常这些问题都有约束,比如子集需要有特定的和,或者子集需要跨越 S 的所有元素。
这让我很难想象如何通过暴力破解来解决这个问题。每次想出一个动态规划table,都无法让它覆盖所有可能的子集排列
这个问题可能像伪多项式时间内的子集和问题一样被解决O(n*summ)
我们用可能的子集和填充数组 0..summ
,当我们遇到相同的和时 - 我们停止。
两个相等的和可能由一些相等的项目组成 - 我们只是删除它们,所以其余的和只包含不同的项目。
Python中的示例使用二进制算术来存储集合(位i+1
对应于使用i-th项求和)。 common
包含相等的位,我们使用异或运算删除它们。
最后几行自行检索所需的集合。
L = [2,5,11,17,29,37,43]
summ = sum(L)
A =[0]*(summ+1)
A[0] = 1
X = 0
Y = 0
for i in range(len(L)):
for k in range(summ, L[i] - 1, -1):
if A[k - L[i]]:
t = A[k - L[i]] | (2 << i)
if A[k]:
common = A[k] & t
X = A[k] ^ common
Y = t ^ common
break
else:
A[k] = t
if X:
break
first = [L[i] for i in range(len(L)) if (X & (2 << i))]
second = [L[i] for i in range(len(L)) if (Y & (2 << i))]
print(first, second)
>>> [2, 11, 29] [5, 37]
在此示例代码中,为 [2, 11, 17, 29] and [5, 17, 37]
找到相等的总和 59
并删除常见的 17
以获得总和 42
的最终结果
并非必须在 A[]
个单元格中存储集合 - 我们可以存储总和的最后一项,然后展开项序列
L = [2,5,11,17,29,37,43]
summ = sum(L)
A =[0]*(summ+1)
A[0] = -1
last = 0
for i in range(len(L)):
for k in range(summ, L[i] - 1, -1):
if A[k - L[i]]:
t = L[i]
if A[k]:
last = k
break
else:
A[k] = t
if last:
break
first = set()
k = last
while k:
first.add(A[k])
k = k - A[k]
second = set()
second.add(t)
k = last - t
while k:
second.add(A[k])
k = k - A[k]
print(first.difference(second),second.difference(first))
>>> {2, 11, 29} {37, 5}
我有这种背包的变体,几乎没有限制,没有限制让我真的不知道从哪里开始。
给定一组正整数。可能: 1 2 3 4 5 6 7 8 9 10 11 12 13
找到两个总和相同的非重叠子集。这两个集合不需要包含 S 中的所有数字。 所以对于前一个例子,答案是 [1,2] 和 [3]
通常这些问题都有约束,比如子集需要有特定的和,或者子集需要跨越 S 的所有元素。 这让我很难想象如何通过暴力破解来解决这个问题。每次想出一个动态规划table,都无法让它覆盖所有可能的子集排列
这个问题可能像伪多项式时间内的子集和问题一样被解决O(n*summ)
我们用可能的子集和填充数组 0..summ
,当我们遇到相同的和时 - 我们停止。
两个相等的和可能由一些相等的项目组成 - 我们只是删除它们,所以其余的和只包含不同的项目。
Python中的示例使用二进制算术来存储集合(位i+1
对应于使用i-th项求和)。 common
包含相等的位,我们使用异或运算删除它们。
最后几行自行检索所需的集合。
L = [2,5,11,17,29,37,43]
summ = sum(L)
A =[0]*(summ+1)
A[0] = 1
X = 0
Y = 0
for i in range(len(L)):
for k in range(summ, L[i] - 1, -1):
if A[k - L[i]]:
t = A[k - L[i]] | (2 << i)
if A[k]:
common = A[k] & t
X = A[k] ^ common
Y = t ^ common
break
else:
A[k] = t
if X:
break
first = [L[i] for i in range(len(L)) if (X & (2 << i))]
second = [L[i] for i in range(len(L)) if (Y & (2 << i))]
print(first, second)
>>> [2, 11, 29] [5, 37]
在此示例代码中,为 [2, 11, 17, 29] and [5, 17, 37]
找到相等的总和 59
并删除常见的 17
以获得总和 42
并非必须在 A[]
个单元格中存储集合 - 我们可以存储总和的最后一项,然后展开项序列
L = [2,5,11,17,29,37,43]
summ = sum(L)
A =[0]*(summ+1)
A[0] = -1
last = 0
for i in range(len(L)):
for k in range(summ, L[i] - 1, -1):
if A[k - L[i]]:
t = L[i]
if A[k]:
last = k
break
else:
A[k] = t
if last:
break
first = set()
k = last
while k:
first.add(A[k])
k = k - A[k]
second = set()
second.add(t)
k = last - t
while k:
second.add(A[k])
k = k - A[k]
print(first.difference(second),second.difference(first))
>>> {2, 11, 29} {37, 5}