您可以尝试使用动态编程来做到这一点。
让dp[k]
等于一个项目列表,大小之和等于k
。最初d[0] = []
和dp[k] = None
为k > 0
. 列表的大小可能受所有元素大小之和的限制,我们称之为S
。
该算法所做的是对每个item
从i = S
下降到的项目i = 0
进行检查dp[i] != None
,这意味着我们知道我们能够选择大小总和等于 的项目i
。这些项目都在清单上dp[i]
。让我们观察一下,我们可以将当前添加item
到该列表中,并拥有一组总和等于 的项目i + item.size
。所以我们分配dp[i + item.size] = dp[i] + [item]
. 处理完所有项目后,我们只需从所需的尺寸总和开始,然后双向寻找最接近的匹配项。
代码:
items = [("ITEM01", 100, 10000), ("ITEM02", 24, 576), \
("ITEM03", 24, 576), ("ITEM04", 51, 2500), ("ITEM05", 155, 25)]
S = sum([item[1] for item in items])
dp = [None for i in xrange(S + 1)]
dp[0] = []
for item in items:
for i in xrange(S, -1, -1):
if dp[i] is not None and i + item[1] <= S:
dp[i + item[1]] = dp[i] + [item]
desired_sum = 150
i = j = desired_sum
while i >= 0 and j <= S:
if dp[i] is not None:
print dp[i]
break
elif dp[j] is not None:
print dp[j]
break
else:
i -= 1
j += 1
输出:
[('ITEM01', 100, 10000), ('ITEM04', 51, 2500)]
然而,这个解决方案的复杂性在于O(n*S)
项目n
的数量和S
大小的总和,所以对于某些目的来说它可能太慢了。在这个解决方案中可以改进的是S
常数。例如,您可以设置S
为,2 * desired_sum
因为我们保证我们可以获取一组大小总和为的项目[0, 2 * desired_sum]
(可能是一个带有 sum 的空集0
)。如果您想至少取一件物品,您可以取所有物品S = max(min_item_size, 2 * desired_sum - min_item_size)
的min_item_size
最小尺寸。
编辑:
哦,当两个组合同样接近时,您还希望获得最大价值desired_size
。然后您必须稍微更改代码以保持每个大小总和的最佳组合。
即:
if dp[i] is not None and i + item[1] <= S:
应该:
if dp[i] is not None and i + item[1] <= S and \
(
dp[i + item[1]] is None
or
sum(set_item[2] for set_item in dp[i]) + item[2]
> sum(set_item[2] for set_item in dp[i + item[1]])
):
(有点难看,但是不知道怎么打断线让它看起来更好看)
当然,您可以保留这些总和以避免每次都计算它们。