算法帮助:如何将数组分成 N 段,最大段最少(平衡分段)
Posted
技术标签:
【中文标题】算法帮助:如何将数组分成 N 段,最大段最少(平衡分段)【英文标题】:Algorithm help: how to divide array into N segments with least possible largest segment (balanced segmenting) 【发布时间】:2015-03-21 15:38:04 【问题描述】:我在一个俄罗斯编程论坛上遇到了这个问题,但还没有想出一个优雅的解决方案。
问题:
你有一个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,更具体地说)。
提前致谢,***ers。
【问题讨论】:
这个问题更适合Programmers Stack Exchange。 @Jordan 为什么? *** help 表示您可以询问软件算法。 Programmers help 表示您可以询问算法和数据结构概念。我可以看到这个问题适合任何一个网站。 如果这是一个家庭作业问题,您应该展示一些代码和自己的基本研究(根据网站定义)。听起来确实像背包问题,顺便说一句。 听起来像是画家的问题:leetcode.com/2011/04/the-painters-partition-problem.html @eckes 这可能是背包问题的变体。 【参考方案1】:让我们对答案进行二分搜索。
对于一个固定的答案X
,很容易检查它是否可行(我们可以使用贪心算法(总是取最长的段,使其总和为<= X
)并比较M
) 的段数。
总时间复杂度为O(N * log(sum of all elements))
。
这是一些伪代码
boolean isFeasible(int[] array, long candidate, int m)
// Here goes the greedy algorithm.
// It finds the minimum number of segments we can get(minSegments).
...
return minSegments <= m;
long getMinimumSum(int[] array, int m)
long low = 0; // too small
long high = sum of elements of the array // definitely big enough
while (high - low > 1)
long mid = low + (high - low) / 2;
if (isFeasible(array, mid, m))
high = mid;
else
low = mid;
return high;
【讨论】:
能否请您扩展您的答案?我没有理解二进制部分,数组没有排序,也不允许。 不错。一个小的优化:如果您发现自己尝试的值 X 小于 SUM/M,您可以跳过该迭代的 O(N) 贪婪通道并立即得出 X 太低的结论,因为在 any 将 SUM 分成 M 部分,必须至少有一个部分 >= SUM/M。 如何证明贪心算法是最优的?【参考方案2】:我喜欢ILoveCoding's approach。这是另一种需要 O(MN^2) 时间的方法,如果 N 和 M 很小但数组中的数字非常大(具体而言,如果 log(sum) >> MN ,这是可能的,但诚然听起来不太现实)。它使用动态规划。
让我们考虑仅将由前 i
计算 f(i, 1) 很容易——这只是前 i 个元素的总和:
f(i, 1) = x[1] + ... + x[i]
要计算 j >= 2 的 f(i, j),请注意元素 i 必须是从某个位置 1 在任何参数 (i, j) 的最优解中,前面的 j-1 个段本身必须是参数 (k-1, j-1) 的最优解。因此,如果我们考虑这个最终段的每个可能的起始位置 k 并取其最佳,我们将计算前 i 个元素的最佳 j 分区:
[编辑 3/2/2015:我们需要取新段的最大值和剩余的最大段,而不是相加!]
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) 时间。
【讨论】:
以上是关于算法帮助:如何将数组分成 N 段,最大段最少(平衡分段)的主要内容,如果未能解决你的问题,请参考以下文章