我需要将一个数组分成k个或更少的子部分,以最小化每个部分的最大总和。
例如,数组包含元素:5,10,21,20
如果k=2
,则可以将数组划分为2个子数组:{5,10,21}
和{20}
。
我们必须返回子数组的最大总和(上例中为36
)。
当k>=4
时,答案将只是数组中最大的元素。
此外,无法更改数组元素的选择顺序,因此我们不能只对数组进行排序并继续。
答案 0 :(得分:6)
这显然是动态编程问题。
分割数组的问题等于在数组中放置k-1个分区。
让你的数组被称为A. 并且让m [i,k]成为A的元素的最佳划分,直到具有k个划分的第i个元素
m[0,*] = 0
m[i,0] = a[i]
m[i,j] = min{m[i-1,j] + a[i], max{m[i-1,j-1], a[i]}}
m [i,j]是在点i处划分阵列或不划分的最小值。
如果计算
,可以找到答案m[length(A), k-1]
你可以从那里回溯分歧。
答案 1 :(得分:3)
如果你想要一个足够好的解决方案,你可以使用一个贪婪的解决方案:
def minmax(lst, k):
lst = sorted(lst, reverse=True) # biggest numbers first is usually better
subs = [[] for _ in xrange(k)] # create the lists for the subarrays
while lst:
subs[0].append(lst.pop(0)) # append to the one with lowest sum
subs.sort(key=sum) # sort by sum (lowest first)
print subs # print the subarrays while creating them
return sum(subs[-1]) # we have sorted by sums, last has the biggest sum
这并不能保证产生最佳效果,但效果非常好。
这是你的例子:
print 'result is %d' % minmax([5, 10, 21, 20], 2)
输出
[[], [21]]
[[20], [21]]
[[21], [20, 10]]
[[21, 5], [20, 10]]
result is 30
嗯,它找到了比你展示的更好的解决方案。
让我们试试k=4
和k=5
>>> print 'result is %d' % minmax([5, 10, 21, 20], 4)
[[], [], [], [21]]
[[], [], [20], [21]]
[[], [10], [20], [21]]
[[5], [10], [20], [21]]
result is 21
>>> print 'result is %d' % minmax([5, 10, 21, 20], 5)
[[], [], [], [], [21]]
[[], [], [], [20], [21]]
[[], [], [10], [20], [21]]
[[], [5], [10], [20], [21]]
result is 21
您还可以将此代码添加到函数的开头,以便在lst
时直接返回k >= len(lst)
的最大值:
def minmax(lst, k):
if k >= len(lst):
return max(lst)
....
这种类型的问题以最小Makespan问题的名称而闻名,搜索它会检索大量有关保证产生最佳结果的算法的信息,但他们的解释也是如此堆栈溢出答案的复杂性,而贪婪的答案很有趣和有启发性。
答案 2 :(得分:1)
更好的想法是在这里使用二进制搜索:
通过使用简单的贪婪算法,您可以测试m(期望的最大总和)的候选值是否在O(n)中是好的:*只需要尽可能长的前缀不超过m,如果关闭则切断,以及重新开始。如果此过程在< = k步骤中停止,那么您已成功对数组进行分区,因此真正的答案是m或更小的值。 *。
因此,您可以对m [0,2 ^ 32 - 1]的值进行二分搜索。
复杂性:O(n * log(sum(array)))
如果sum(数组)与n相当,则此算法将以线性时间运行。