将数组划分为k个或更少的子部分,以最小化每个部分的最大总和

时间:2014-08-17 19:04:48

标签: arrays dynamic-programming

我需要将一个数组分成k个或更少的子部分,以最小化每个部分的最大总和。

例如,数组包含元素:5,10,21,20

如果k=2,则可以将数组划分为2个子数组:{5,10,21}{20}。 我们必须返回子数组的最大总和(上例中为36)。

k>=4时,答案将只是数组中最大的元素。

此外,无法更改数组元素的选择顺序,因此我们不能只对数组进行排序并继续。

3 个答案:

答案 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

这并不能保证产生最佳效果,但效果非常好。

k = 2

这是你的例子:

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=4k=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问题

这种类型的问题以最小Makespan问题的名称而闻名,搜索它会检索大量有关保证产生最佳结果的算法的信息,但他们的解释也是如此堆栈溢出答案的复杂性,而贪婪的答案很有趣和有启发性。

答案 2 :(得分:1)

更好的想法是在这里使用二进制搜索:

通过使用简单的贪婪算法,您可以测试m(期望的最大总和)的候选值是否在O(n)中是好的:*只需要尽可能长的前缀不超过m,如果关闭则切断,以及重新开始。如果此过程在< = k步骤中停止,那么您已成功对数组进行分区,因此真正的答案是m或更小的值。 *。

因此,您可以对m [0,2 ^ 32 - 1]的值进行二分搜索。

复杂性:O(n * log(sum(array)))

如果sum(数组)与n相当,则此算法将以线性时间运行。