算法帮助:如何将数组划分为具有最小可能最大段的N个段(平衡分段)

时间:2015-01-22 18:01:36

标签: arrays algorithm optimization

我在俄罗斯的一个编程论坛上遇到过这个问题,但没有找到一个优雅的解决方案。

问题:

您有一个 N正整数的数组,您需要将其划分为 M个连续段,以便最大段的总和是可能的最小值。按段的总数,我的意思是所有整数的总和。换句话说,我想要一个均衡的数组分割,你不希望单个分段过大。

示例:

  • 数组:[4,7,12,5,3,16]

  • M = 3,这意味着我需要将我的数组分成3个子阵列。

  • 解决方案将是:[4,7] [12,5] [3,16]因此最大的片段是[3,16] = 19并且没有其他分割变体可以产生具有更小的最大片段总

另一个例子:

  • 数组[3,13,5,7,18,8,20,1]
  • M = 4

解决方案:[3,13,5] [7,18] [8] [20,1],"最胖"段是[7,18] = 25(纠正我,如果我错了,我编写了这个例子)

我有一种感觉,这是一些经典的CS /数学问题,可能还有一些名人与之相关的名字,比如Dijkstra的问题。   - 有没有任何已知的解决方案?   - 如果没有,你能否提出除暴力强迫之外的其他解决方案,据我所知时间复杂度,指数。(N ^ M,更具体)。

提前致谢,stackoverflowers。

2 个答案:

答案 0 :(得分:5)

  1. 让我们对答案进行二元搜索。

  2. 对于固定答案X,很容易检查它是否可行(我们可以使用贪婪算法(总是使用最长的段,使其总和为{{1} })并将段数与<= X)进行比较。

  3. 总时间复杂度为M

    这是一些伪代码

    O(N * log(sum of all elements))

答案 1 :(得分:2)

我喜欢ILoveCoding's approach。这是采用 O(MN ^ 2)时间的另一种方式,如果N和M很小但数组中的数字非常大(具体来说,如果log(sum)&gt;&gt; ; MN,这是可能的但不可否认听起来不太现实)。它使用动态编程。

让我们考虑将由第一个i&lt; = N个条目组成的子阵列划分为j <= M个段。设f(i,j)是该子问题的最佳解中最大段的权重 - 即,在所有这样的分区中最大段最小的第一i个数的j分区中的最大段的权重。我们想要计算f(N,M),以及与它对应的(可能有多个)分区。

计算f(i,1)很容易 - 这只是前i个元素的总和:

f(i, 1) = x[1] + ... + x[i]

要为j> = 2计算f(i,j),请观察该元素i必须是从某个位置1&lt; = k&lt; = i开始的某个段的最后一个元素,并且其前面是j-1个段 - 并且在参数(i,j)的任何最优解中,那些j-1个前面的段必须本身对于参数(k-1,j-1)是最佳的。因此,如果我们考虑最后一个段的每个可能的起始位置k并采取最佳,我们将计算前i个元素的最佳j分区:

[2015年3月2日编辑:我们需要占用新细分和最大剩余细分的最大值,而不是添加它们!]

f(i, j >= 2) = minimum of (max(f(k-1, j-1), x[k] + ... + x[i])) over all 1 <= k <= i

如果我们按递减顺序尝试k值,那么我们可以很容易地在每k值的恒定时间内建立和,因此计算单个f(i,j)值需要O(N)时间。我们要计算这些值的MN,因此所需的总时间为O(MN ^ 2)。

还需要一个边界条件来禁止尝试分割成比元素更多的片段:

f(i, j > i) = infinity

一旦我们计算了f(N,M),我们就可以通过以常规方式回溯DP矩阵来提取相应的分区 - 但在这种情况下,使用ILoveCoding的贪心算法构建分区可能更容易。无论哪种方式都花费O(N)时间。