需要解决这个算法难题的想法

发布于 2024-12-22 13:52:21 字数 499 浏览 4 评论 0原文

我过去遇到过一些与此类似的问题,但我仍然不知道如何解决这个问题。问题是这样的:

给你一个正整数数组,大小为 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 个子数组。然而,这是指数解决方案,永远不会起作用。

所以我正在寻找好的想法来解决它。如果你有的话请告诉我。

感谢您的帮助。

I've came across some similar problems to this one in the past, and I still haven't got good idea how to solve this problem. Problem goes like this:

You are given an positive integer array with size n <= 1000 and k <= n which is the number of contiguous subarrays that you will have to split your array into. You have to output minimum m, where m = max{s[1],..., s[k]}, and s[i] is the sum of the i-th subarray. All integers in the array are between 1 and 100. Example :

Input:                           Output:
5  3  >> n = 5 k = 3             3
2 1 1 2 3

Splitting array into 2+1 | 1+2 | 3 will minimize the m.

My brute force idea was to make first subarray end at position i (for all possible i) and then try to split the rest of the array in k-1 subarrays in the best way possible. However, this is exponential solution and will never work.

So I'm looking for good ideas to solve it. If you have one please tell me.

Thanks for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

离线来电— 2024-12-29 13:52:21

您可以使用动态规划来解决这个问题,但实际上您可以通过对答案进行贪心和二分搜索来解决。该算法的复杂度为 O(n log d),其中 d 是输出答案。 (上限是数组中所有元素的总和。)(或输出位大小的 O( nd )

这个想法是对您的 进行二分搜索m 将是 - 然后在数组上贪婪地向前移动,将当前元素添加到分区中,除非添加当前元素将其推到当前 m 之上 - 在这种情况下,您启动一​​个新分区。如果使用的分区数量小于或等于给定的输入k,则当前的m 成功(从而调整您的上限)。否则,您使用了太多分区,并提高 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 不太大,您实际上可以跳过二分搜索并进行线性搜索:

 for i = 0 to infinity
    if partitions( array, i ) <= k
       return i

You can use dynamic programming to solve this problem, but you can actually solve with greedy and binary search on the answer. This algorithm's complexity is O(n log d), where d is the output answer. (An upper bound would be the sum of all the elements in the array.) (or O( n d ) in the size of the output bits)

The idea is to binary search on what your m would be - and then greedily move forward on the array, adding the current element to the partition unless adding the current element pushes it over the current m -- in that case you start a new partition. The current m is a success (and thus adjust your upper bound) if the numbers of partition used is less than or equal to your given input k. Otherwise, you used too many partitions, and raise your lower bound on m.

Some pseudocode:

// 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
 }

This should be easier to come up with conceptually. Also note that because of the monotonic nature of the partitions function, you can actually skip the binary search and do a linear search, if you are sure that the output d is not too big:

 for i = 0 to infinity
    if partitions( array, i ) <= k
       return i
帅气称霸 2024-12-29 13:52:21

动态规划。创建一个数组

int best[k+1][n+1];

,其中 best[i][j] 是最好的分割数组 int i 子数组的前 j 元素的方法。 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 =上限(sum/k)。这是最小值的下限。要找到最小值的良好上限,请通过任何提到的方法创建一个良好的分区,移动边界,直到找不到任何仍会减少最大总和的简单移动。这给了你一个上限 B,不比下限大很多(如果它大得多,我认为通过移动边框你会发现一个简单的改进)。
现在继续 ChingPing 的算法,利用已知的上限减少可能的分支数量。最后一个阶段的时间复杂度为 O((BA)*n),发现 B 未知,但我猜比 O(n^2) 更好。

Dynamic programming. Make an array

int best[k+1][n+1];

where best[i][j] is the best you can achieve splitting the first j elements of the array int i subarrays. best[1][j] is simply the sum of the first j array elements. Having row i, you calculate row i+1 as follows:

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] will contain the solution. The algorithm is O(n^2*k), probably something better is possible.

Edit: a combination of the ideas of ChingPing, toto2, Coffee on Mars and rds (in the order they appear as I currently see this page).

Set A = ceiling(sum/k). This is a lower bound for the minimum. To find a good upper bound for the minimum, create a good partition by any of the mentioned methods, moving borders until you don't find any simple move that still decreases the maximum subsum. That gives you an upper bound B, not much larger than the lower bound (if it were much larger, you'd find an easy improvement by moving a border, I think).
Now proceed with ChingPing's algorithm, with the known upper bound reducing the number of possible branches. This last phase is O((B-A)*n), finding B unknown, but I guess better than O(n^2).

心是晴朗的。 2024-12-29 13:52:21

我有一个糟糕的分支定界算法(请不要对我投反对票)

首先将数组和 dvide 的总和乘以 k,这为您提供了答案的最佳情况限制,即平均 A。此外,我们将保留迄今为止看到的最佳解决方案对于任何分支 GO(全局最优)。让我们考虑在某个数组元素后面放置一个分隔符(逻辑)作为分区单元,并且我们必须放置 k-1 个分区。现在我们将以这种方式贪婪地放置分区,

遍历数组元素将它们相加,直到您看到在下一个位置我们将超过 A,现在创建两个分支,一个将分隔符放置在该位置,另一个分支放置在下一个位置位置,递归地执行此操作并设置 GO = min(GO,回答分支)。
如果在任何分支中的任何点,我们有一个大于 GO 的分区,或者位置数小于我们绑定的剩余分区。最后你的回答应该是“GO”。

编辑:
正如 Daniel 所建议的,我们可以稍微修改分隔符放置策略,直到达到元素总和为 A 或剩余位置少于分隔符为止。

I have a sucky branch and bound algorithm ( please dont downvote me )

First take the sum of array and dvide by k, which gives you the best case bound for you answer i.e. the average A. Also we will keep a best solution seen so far for any branch GO ( global optimal ).Lets consider we put a divider( logical ) as a partition unit after some array element and we have to put k-1 partitions. Now we will put the partitions greedily this way,

Traverse the array elements summing them up until you see that at the next position we will exceed A, now make two branches one where you put the divider at this position and other where you put at next position, Do this recursiely and set GO = min (GO, answer for a branch ).
If at any point in any branch we have a partition greater then GO or the no of position are less then the partitions left to be put we bound. In the end you should have GO as you answer.

EDIT:
As suggested by Daniel, we could modify the divider placing strategy a little to place it until you reach sum of elements as A or the remaining positions left are less then the dividers.

独孤求败 2024-12-29 13:52:21

这只是一个想法的草图......我不确定它是否有效,但它非常简单(而且可能也很快)。

你可以从均匀分布间隔开始(实际上如何开始并不重要)。

对每个子数组求和。
找到总和最大的子数组。
查看左右相邻子数组,如果左侧子数组的总和小于右侧子数组的总和,则将左侧的间隔移动 1(反之亦然)。
对当前总和最大的子数组重做。

你会遇到一些情况,你会不断地在相同的两个位置之间跳跃,这可能意味着你有解决方案。

编辑:请参阅@rds的评论。您必须更加努力地考虑弹跳解决方案和最终条件。

This is just a sketch of an idea... I'm not sure that it works, but it's very easy (and probably fast too).

You start say by putting the separations evenly distributed (it does not actually matter how you start).

Make the sum of each subarray.
Find the subarray with the largest sum.
Look at the right and left neighbor subarrays and move the separation on the left by one if the subarray on the left has a lower sum than the one on the right (and vice-versa).
Redo for the subarray with the current largest sum.

You'll reach some situation where you'll keep bouncing the separation between the same two positions which will probably mean that you have the solution.

EDIT: see the comment by @rds. You'll have to think harder about bouncing solutions and the end condition.

月寒剑心 2024-12-29 13:52:21

我的想法,不幸的是行不通:

  1. 将数组拆分为 N 个子数组
  2. 找到总和最小的两个连续子数组
  3. 将步骤 2 中找到的子数组合并,形成一个新的连续子数组
  4. 如果子数组总数大于 k,则迭代从步骤 2 开始,否则完成。

My idea, which unfortunately does not work:

  1. Split the array in N subarrays
  2. Locate the two contiguous subarrays whose sum is the least
  3. Merge the subarrays found in step 2 to form a new contiguous subarray
  4. If the total number of subarrays is greater than k, iterate from step 2, else finish.
琉璃繁缕 2024-12-29 13:52:21

如果你的数组有随机数,你可以希望每个子数组都有 n/k 的分区是一个很好的起点。

从那里

  1. 通过计算和
  2. 存储该候选解决方案来评估该候选解决方案。例如:
    • 每个子数组的索引数组
    • 子数组的总和的相应最大值
  3. 减少最大子数组的大小:创建两个新候选者:一个子数组从索引+1开始;另一个子数组从index+1开始。子数组以索引 1 结尾的一个
  4. 评估新候选者。
    • 如果它们的最大值更高,则丢弃
    • 如果它们的最大值较低,则迭代 2,除非该候选值已被评估,在这种情况下它就是解决方案。

If your array has random numbers, you can hope that a partition where each subarray has n/k is a good starting point.

From there

  1. Evaluate this candidate solution, by computing the sums
  2. Store this candidate solution. For instance with:
    • an array of the indexes of every sub-arrays
    • the corresponding maximum of sum over sub-arrays
  3. Reduce the size of the max sub-array: create two new candidates: one with the sub-array starting at index+1 ; one with sub-array ending at index-1
  4. Evaluate the new candidates.
    • If their maximum is higher, discard
    • If their maximum is lower, iterate on 2, except if this candidate was already evaluated, in which case it is the solution.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文