我过去曾经遇到过类似的问题,我仍然不知道如何解决这个问题。问题是这样的:
您将获得一个正整数数组,其大小为n <= 1000且k <= n,这是您必须将数组拆分为的连续子数组的数量。你必须输出最小m,其中m = max {s [1],...,s [k]},s [i]是第i个子阵列的总和。数组中的所有整数都在1到100之间。例如:
Input: Output:
5 3 >> n = 5 k = 3 3
2 1 1 2 3
将数组拆分为2 + 1 | 1 + 2 | 3将最小化m。
我的蛮力想法是让第一个子阵列在位置i(对于所有可能的i)结束,然后尝试以尽可能最好的方式将数组的其余部分分成k-1个子阵列。但是,这是指数级解决方案,永远不会奏效。
所以我正在寻找好的想法来解决它。如果你有,请告诉我。
感谢您的帮助。
答案 0 :(得分:5)
您可以使用动态编程来解决此问题,但实际上您可以通过贪婪和二元搜索来解决问题。此算法的复杂度为O(n log d)
,其中d
是输出答案。 (上限是数组中所有元素的总和。)(或输出位大小为O( n d )
)
我们的想法是二元搜索你的m
- 然后贪婪地向前推进数组,将当前元素添加到分区,除非添加当前元素将其推送到当前{{1} - 在这种情况下,您启动一个新分区。如果使用的分区数小于或等于给定输入m
,则当前m
成功(因此调整上限)。否则,您使用了太多分区,并在k
上提高了下限。
一些伪代码:
m
这在概念上应该更容易提出。另请注意,由于分区功能的单调性质,如果您确定输出// binary search
binary_search ( array, N, k ) {
lower = max( array ), upper = sum( array )
while lower < upper {
mid = ( lower + upper ) / 2
// if the greedy is good
if partitions( array, mid ) <= k
upper = mid
else
lower = mid
}
}
partitions( array, m ) {
count = 0
running_sum = 0
for x in array {
if running_sum + x > m
running_sum = 0
count++
running_sum += x
}
if running_sum > 0
count++
return count
}
不是太大,您实际上可以跳过二分查找并进行线性搜索:
d
答案 1 :(得分:3)
动态编程。制作一个数组
int best[k+1][n+1];
其中best[i][j]
是最好的,你可以实现分割数组int j
子数组的第一个i
元素。 best[1][j]
只是第一个j
数组元素的总和。拥有行i
,您可以按如下方式计算行i+1
:
for(j = i+1; j <= n; ++j){
temp = min(best[i][i], arraysum[i+1 .. j]);
for(h = i+1; h < j; ++h){
if (min(best[i][h], arraysum[h+1 .. j]) < temp){
temp = min(best[i][h], arraysum[h+1 .. j]);
}
}
best[i+1][j] = temp;
}
best[m][n]
将包含解决方案。算法是O(n ^ 2 * k),可能更好。
编辑:ChingPing,toto2,Coffee on Mars和rds(按照我目前看到此页面时的顺序)的想法的组合。
设置A = ceiling(sum/k)
。这是最小值的下限。要找到最小值的上限,可以通过任何提到的方法创建一个好的分区,移动边框,直到找不到仍然会减少最大子数的任何简单移动。这给你一个上限B,没有比下限大得多(如果它更大,你会发现通过移动边界很容易改善,我想)。
现在继续使用ChingPing的算法,已知的上限减少了可能的分支数。最后一个阶段是O((B-A)* n),发现B
未知,但我猜想比O(n ^ 2)好。
答案 2 :(得分:2)
我有一个糟糕的分支和绑定算法(请不要低估我)
首先将数组和dvide的总和取为k,这为你提供了最佳的答案范围,即平均值A.此外,我们将保留迄今为止任何分支GO(全局最优)的最佳解决方案。让我们考虑一下我们在一些数组元素之后放置一个分隔符(逻辑)作为分区单元,我们必须放置k-1个分区。现在我们将以这种方式贪婪地放置分区,
遍历数组元素,将它们相加,直到您看到在下一个位置我们将超过A,现在将两个分支放在一个将分隔符放在此位置的位置,另一个放置在下一个位置的位置,执行此操作并递归GO = min(GO,回答分支)。 如果在任何分支的任何一点,我们有一个更大的分区,那么GO或位置的数量小于那么分区留给我们绑定。最后,当你回答时,你应该有GO。
修改强> 正如丹尼尔所建议的那样,我们可以稍微修改分配器放置策略,直到你将元素总和作为A或剩下的剩余位置小于分隔符。
答案 3 :(得分:1)
这只是一个想法的草图......我不确定它是否有效,但它很容易(也可能很快)。
你开始说分离均匀分布(实际上你的开始并不重要)。
制作每个子阵列的总和
找到总和最大的子阵列
如果左侧的子阵列的总和低于右侧的子阵列(反之亦然),请查看右侧和左侧的邻居子阵列并将左侧的分隔移动一个。
用当前最大总和重做子阵列。
你会遇到一些情况,你会在相同的两个位置之间保持分离,这可能意味着你有解决方案。
编辑:请参阅@rds的评论。你必须更加努力地思考弹跳解决方案和最终条件。
答案 4 :(得分:0)
如果你的数组有随机数,你可以希望每个子数组有n / k的分区是一个很好的起点。
从那里
答案 5 :(得分:0)
我的想法,遗憾的是不起作用: