这是一个递归解决方案,使用 Python 2.7:
def fill(length, sublengths):
# IMPORTANT: this function will produce INCORRECT RESULTS if sublengths
# is not a list of unique integers sorted increasingly.
fillings = []
for i, sublength in enumerate(sublengths):
if sublength > length:
# if sublength is greater than length, there are no more allowable
# fillings (because sublengths are unique and are sorted
# increasingly), so we return the fillings collected so far;
return fillings
elif sublength == length:
# if sublength is exactly equal to length, then only one filling is
# possible, namely [sublength]; we append this filling to the
# fillings;
fillings.append([sublength])
else:
# we generate all the fillings that begin with sublength by
# prepending sublength to all the allowable fillings of
# (length - sublength), which we obtain by making a recursive call.
fillings.extend([[sublength] + subresult
for subresult in
fill(length - sublength, sublengths[i:])])
例子:
In [2]: fill(15, [3, 4, 5, 6, 7, 10])
Out[2]:
[[3, 3, 3, 3, 3],
[3, 3, 3, 6],
[3, 3, 4, 5],
[3, 4, 4, 4],
[3, 5, 7],
[3, 6, 6],
[4, 4, 7],
[4, 5, 6],
[5, 5, 5],
[5, 10]]
顺便说一句:fill(70, [3, 4, 5, 6, 7, 10])) 产生 1657 种可能的填充物,因此您可能需要一些额外的标准来减少替代品。
一些注意事项:
为了避免重复解决方案,我们将要求每次灌装时顺序递增;
关键思想是这样的:假设要填充的长度是L,并且a1 a 2an 是可用的子长度。找出所有以a1开头的L可能的填充物,就等于在前面加上a1 到 L - a1 的所有填充物。这是fill 的else 块中递归调用的基本原理。 (当一个函数调用自己时,就像fill 所做的那样,该函数被称为递归。)
-
由于fill 要求sublengths 不重复且排序越来越多,我们可以使用以下前端函数来确保满足这些条件:
def do_fill(长度,子长度):
返回填充(长度,排序(设置(子长度)))
(注意:下面是对代码作用的相当详细的解释。如果您已经理解代码,则可以放心地跳过本文的其余部分。)
为了更好地了解发生了什么,回到上面的例子,然后根据第一个子长度对解决方案进行分组;您将获得如下所示的三个组:
# group I
[3, 3, 3, 3, 3]
[3, 3, 3, 6]
[3, 3, 4, 5]
[3, 4, 4, 4]
[3, 5, 7]
[3, 6, 6]
# group II
[4, 4, 7]
[4, 5, 6]
# group III
[5, 5, 5]
[5, 10]
现在,使用子长度 [3, 4, 5, 6, 7, 10] 比较第 I 组中的填充物,所有填充物均以 3 开头,与 15-3 = 12 的填充物:
In [3]: fill(15 - 3, [3, 4, 5, 6, 7, 10])
Out[3]:
[[3, 3, 3, 3],
[3, 3, 6],
[3, 4, 5],
[4, 4, 4],
[5, 7],
[6, 6]]
如果现在你在所有这些填充物前加上 3,你将得到第一组的填充物。
现在考虑第 II 组中的填充物,所有填充物都以 4 开头。使用子长度 [4, 5, 6, 7, 10] 将它们与 15 - 4 = 11 的填充物进行比较:
In [4]: fill(15 - 4, [4, 5, 6, 7, 10])
Out[4]:
[4, 7],
[5, 6]
同样,如果您在所有这些填充物前加上 4,您将得到第 II 组中的填充物。
您可能想知道,为什么我在上面对fill 的最后一次调用中使用 [4, 5, 6, 7, 10] 作为子长度,而不是 [3, 4, 5, 6, 7, 10] ?这是因为我只对越来越多的以 4 开头的馅料感兴趣。这排除了任何包括 3 的馅料。
最后,要获得第 III 组中的填充物,在 15 - 5 = 10 的所有填充物前面加上 5,使用子长度 [5, 6, 7, 10]:
In [5]: fill(15 - 5, [5, 6, 7, 10])
Out[5]:
[[5, 5],
[10]]
如果您愿意,可以对每个子组重复相同类型的分析。例如,可以将fill(15 - 3, [3, 4, 5, 6, 7, 10])生成的填充物按照他们的第一个元素进行分组;你会得到 4 个组:
[3, 3, 3, 3]
[3, 3, 6]
[3, 4, 5]
[4, 4, 4]
[5, 7]
[6, 6]
这些组是通过将 3、4、5 或 6 分别添加到由
fill((15 - 3) - 3, [3, 4, 5, 6, 7, 10])
fill((15 - 3) - 4, [ 4, 5, 6, 7, 10])
fill((15 - 3) - 5, [ 5, 6, 7, 10])
fill((15 - 3) - 6, [ 6, 7, 10])
上面的分析只是“手动”完成了 fill 函数所做的事情。
需要注意的重要一点是,每次递归调用,问题都会变得更简单。
例如,在生成填充 [3, 5, 7] 的过程中,对fill 的以下调用被执行:
fill(15, [3, 4, 5, 6, 7, 10]) = fill(15, [3, 4, 5, 6, 7, 10])
fill(15 - 3, [3, 4, 5, 6, 7, 10]) = fill(12, [3, 4, 5, 6, 7, 10])
fill(15 - 3 - 5, [ 5, 6, 7, 10]) = fill( 7, [ 5, 6, 7, 10])
特别注意最后一个,fill(7, [5, 6, 7, 10])。可以通过检查发现其解决方案:从子长度 [5, 6, 7, 10] 中只能填充 7 个。递归总是以这些平凡案例的解决方案结束。最终的解决方案是由这些琐碎的解决方案组合而成的。