【问题标题】:Split up an int array into 3 parts, maximize the size of the 2 smaller parts将一个 int 数组拆分为 3 个部分,最大化 2 个较小部分的大小
【发布时间】:2017-05-10 18:33:03
【问题描述】:

给定一个长度为 nint 数组,将数组分成 3 部分,并确保 2 个较小的部分尽可能大。

拆分规则:

  • 选择给定数组的两个索引ab (0
  • 第一部分的大小是:从索引 0 到 a-1(含)所有数组条目的总和
  • 第二部分的大小是:从索引 a 到 b(含)所有数组条目的总和
  • 第三部分的大小为:从 b+1 到 n-1(含)所有数组项的总和
  • 可能有空的部分..

预期的输出是两个较小部分的总和(它们的大小)。

例如,n = 6 的数组和一些值被给出。 该解决方案计算a = 2b = 3,将数组分成三部分:左边部分的大小为6 + 7 = 13,中间部分为8 + 9 = 17,右边部分为4 + 5 = 9。输出为13 + 9 = 22(两个较小部分的总和)。

图形表示:

更多示例:

[6, 8, 3, 5, 7, 2, 4, 6] 应该拆分成:

  1. 左 (6 + 8 = 14)
  2. 中间 (3 + 5 + 7 = 15)
  3. 对 (2 + 4 + 6 = 12)

输出为14 + 12 = 26(两个较小部分的总和)

[9, 12, 4, 7, 10, 2, 5, 8, 11, 3] 应拆分为:

  1. 左 (9 + 12 + 4 = 25)
  2. 中间 (7 + 10 + 2 + 5 = 24)
  3. 对 (8 + 11 + 3 = 22)

输出为22 + 24 = 46(两个较小部分的总和)

我的方法不适用于给定的测试用例:

// L is size of left part, M is size of middle part, R is size of right part
/* I start with all array entries in the middle part, then I put elements 
   out of the middle part into the left and right part (depending on which 
   is smaller) until one of them is larger than M, this approach works for 
   many cases, two exceptions are the first 2 arrays given as examples in 
   this post. 
*/
       long a = 1;
       long b = n;
       long L = R = 0;
       long M = arr.sumOfAllArrayEntries;
       long temp;
       long[] arr = {9, 12, 4,  7, 10, 2, 5, 8, 11, 3}; 

       while (M > Math.max(L, R)) {
            if (L < R) {
                // move leftmost element of M to L
                temp = arr[(int) a++];

                M -= temp;
                L += temp;

            }
            else {
                // move rightmost element of M to R
                temp = arr[(int) b--];

                M -= temp;
                R += temp;
            }
        }

        // finds maximum of M, L, R 
        temp = Math.max(M, Math.max(L, R));

        // finds 2 smallest numbers out of M, L, R 
        if (temp == M)
            temp = L + R;
        else if (temp == L)
            temp = M + R;
        else if (temp == R)
            temp = M + L;

        // temp is equal to the sum of the 2 smaller parts
        System.out.println("Output: " + temp);

【问题讨论】:

    标签: java arrays algorithm


    【解决方案1】:

    想到的基本思路:

    • 循环遍历a 的所有位置。
      • 遍历b 的所有位置。
        • 计算左、中、右总和。
        • 计算目标总和,如果它比我们见过的最佳总和更好,则将其存储。

    这可以通过注意以下几点优化为O(n)

    • 对于任何给定位置ba 的最佳位置将始终是,总和,在b 的中间和开始(特别是在最小化左侧和中间之间的差异的点)总和)。
    • a 的最佳位置不能向左移动,因为 b 向右移动(因为这样会减少左侧总和以获得更大的中间总和,从而降低目标总和)。
    • 这意味着我们只需要在b 上循环一次,同时跟踪a,并在适当的时候增加它。
    • 我们可以随时跟踪总和。

    这给了我们以下代码:

    int arr[] = {9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
    int sum = 0;
    for (int i: arr)
        sum += i;
    int a = 0;
    int left = 0, mid = 0;
    int best = 0;
    for (int b = 0; b < arr.length; b++)
    {
        mid += arr[b];
        // since this loop increases `a` with every iteration,  and `a` never resets,
        // it will not run more than O(n) times in total
        while (a < b && Math.min(left + arr[a], mid - arr[a]) > Math.min(left, mid))
        {
            left += arr[a];
            mid -= arr[a];
            a++;
        }
        int right = sum - mid - left;
        best = Math.max(best,
                        mid + left + right - Math.max(mid, Math.max(left, right)));
    }
    System.out.println(best);
    

    Live demo.


    您的方法的问题是当您遇到如下情况时:

         a      b
    6  7 | 8  9 | 4  5
    L=13   M=17   R=9
    

    M &gt; Math.max(L, R) 为真,因此您将移动其中一个元素,尽管已经进行了最佳拆分。

    注意我是如何在我的代码中使用Math.min(left + arr[a], mid - arr[a]) &gt; Math.min(left, mid) 而不是简单的left &lt; mid。您将需要类似的东西来检查您是否应该继续。

    您需要考虑的一个示例是您需要进一步增加更大的一侧:

    100 | 10 120 | 90 -> 100 10 | 120 | 90
    

    这可能会使您的代码更加复杂。

    【讨论】:

    • 尝试[4, 3, 2, 9, 8],输出应该是17(拆分为4+3+2=9,9,8)。您的算法返回 15。oldbest 变量似乎也没有必要。有趣的方法!
    • @AnnaVopureta 不错,已编辑 - 这应该是一个 while 循环而不是 if 语句。 oldbest 只是一个我忘记的调试变量。
    【解决方案2】:

    这可以使用Two Pointers 的概念来完成。所以首先将主数组视为3 子数组的串联。 ABC。现在我们可以先计算数组所有元素的总和,这表明考虑的数组包含所有元素。

    所以现在我们需要跟踪原始数组的 3 个连续子数组的总和。考虑这里我们有 3 个数组作为

    A  ---> Starting from the left-side (index 0)
    B  ---> Middle sub-array
    C  ---> Starting from the right-side (index n-1)
    

    这里的答案应该是min(sumOfA,min(sumOfB,sumOfC)),这是要最大化的。

    这里我们存储了子数组B 中所有元素的总和,考虑到它具有数组的所有元素。 AC 为空。现在我们将从任一端逐个删除元素并将该值添加到适当的子数组AC,我们需要通过减去它从B 中删除它。

    现在的问题仍然是要删除哪个元素。为此,我们将检查 AC 的值,如果总和小于另一个,我们将从该端添加元素到特定的子数组。

    这里可能出现的另一个问题是终止条件。这里的终止条件是Sum of B &gt; Sum of A &amp;&amp; Sum of B &gt; Sum of C。因此,当B 的总和小于其他两个子数组中的任何一个时,我们需要停在那里。

    这种方法的复杂性:O(n)

    代码:

    import java.util.*;
    
    class Main
    {
        public static void main(String args[])
        {
            long arr[]={9, 12, 4, 7, 10, 2, 5, 8, 11, 3};
    
            long sumOfA=0;
            long sumOfB=0;
            long sumOfC=0;
    
            int a = 0;             //set end of sub-array A
            int b = arr.length-1;  //set start of sub-array-C
    
            long maximum =0; // Minimum of sum of all subarrays should be maximum, 
                             // That will be sufficient to get the answer
    
            long answer=0;
            int answer_a=0;
            int answer_b=0;
    
            for(int i=0;i<arr.length;i++)
            {
                sumOfB+=arr[i];
            }
    
            for(int i=0;i<arr.length;i++)
            {
                long minimum = Math.min(sumOfA , Math.min(sumOfB,sumOfC));
    
                if(minimum>=maximum)
                {
                    answer_a=a;
                    answer_b=b;
    
                    ArrayList<Long> list=new ArrayList<Long>();     //To calculate the answer
                    list.add(sumOfA);
                    list.add(sumOfB);
                    list.add(sumOfC);
                    Collections.sort(list);
    
                    answer=Math.max(answer,list.get(0)+list.get(1));         //take minimum two elements
                    maximum=minimum;
                } 
    
                if(sumOfB < sumOfC || sumOfB < sumOfA)
                    break;
    
                if(a>=b)                //If both pointer passes to each other
                    break;
    
                if(sumOfA == sumOfC)
                {
                    if(arr[a]<arr[b])   //take minimum element
                    {
                        sumOfA+=arr[a];
                        sumOfB-=arr[a];
                        a++;            //move a to next element 
                    }
                    else
                    {
                        sumOfC+=arr[b];
                        sumOfB-=arr[b];
                        b--;            //move b to prev element
                    }
                }
                else if(sumOfA > sumOfC)
                {
                    sumOfC+=arr[b];
                    sumOfB-=arr[b];
                    b--;
                }
                else
                {
                    sumOfA+=arr[a];
                    sumOfB-=arr[a];
                    a++;
                }
            }
    
            System.out.println("a(exclsive) : "+answer_a);
            System.out.println("b(exclsive) : "+answer_b);
            System.out.println("Answer : "+answer);
        }
    }
    

    [9, 12, 4, 7, 10, 2, 5, 8, 11, 3] 的答案:

    a(exclsive) : 3
    b(exclsive) : 6
    Answer : 46
    

    [6, 8, 3, 5, 7, 2, 4, 6] 的答案:

    a(exclsive) : 2
    b(exclsive) : 4
    Answer : 26
    

    【讨论】:

    • 你甚至用你的长期处理大量输入,非常有趣的方法!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-27
    • 2013-06-22
    • 1970-01-01
    • 2021-03-28
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多