将int数组拆分为3个部分,最大化2个较小部分的大小

时间:2017-05-10 18:33:04

标签: java arrays algorithm

给定长度为int的{​​{1}}数组,将数组拆分为3个部分,并确保2个较小的部分尽可能大。

分裂规则:

  • 选择给定数组的两个索引na(0 <= a&lt; = b&lt; = n-1)
  • 第一部分的大小是:从索引0到a-1(包括)的所有数组条目的总和
  • 第二部分的大小是:从索引a到b(包括)的所有数组条目的总和
  • 第三部分的大小是:从b + 1到n-1(包括)的所有数组条目的总和
  • 空的部分是可能的..

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

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

图形表示:

Graphical representation of a wanted array split

更多例子:

[6,8,3,5,7,2,4,6]应分为:

  1. 左(13 + 9 = 22
  2. 中(6 + 8 = 14
  3. 右(3 + 5 + 7 = 15
  4. 输出为2 + 4 + 6 = 12(2个较小部分的总和)

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

    1. 左(14 + 12 = 26
    2. 中(9 + 12 + 4 = 25
    3. 右(7 + 10 + 2 + 5 = 24
    4. 输出为8 + 11 + 3 = 22(2个较小部分的总和)

      我的方法对于给定的测试用例不起作用:

      22 + 24 = 46

2 个答案:

答案 0 :(得分:3)

想到的基本想法:

  • 循环b的所有位置。
    • 循环O(n)的所有位置。
      • 计算左,中,右总和。
      • 计算目标总和并存储它,如果它比我们见过的最佳总和好。

通过注意以下几点,可以优化b

  • 对于任何给定的位置ab的最佳位置总是在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); ,并在适当的时候增加它。
  • 我们可以随时跟踪总和。

这给了我们以下代码:

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

Live demo

您遇到的问题是当您遇到以下情况时:

M > Math.max(L, R)

Math.min(left + arr[a], mid - arr[a]) > Math.min(left, mid)将是正确的,因此您将移动其中一个元素,尽管已经进行了最佳分割。

请注意我在代码中的left < mid而不仅仅是100 | 10 120 | 90 -> 100 10 | 120 | 90 。你需要类似的东西来检查你是否应该继续。

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

import rollup from 'rollup'

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

答案 1 :(得分:1)

这可以使用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 > Sum of A && Sum of B > 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