计算将数组划分为具有相等和的3个连续部分的方法的数量

时间:2015-01-13 18:49:37

标签: arrays partition

我有一个数组,我必须计算多种方法将它分成3个连续的部分,使它们的总和相等。如何修改分区问题呢?
例如 -
A 成为包含 {1,2,3,0,3} 的数组 回答 - 2
因为它可以分为 {{1,2},{3},{0,3}} {{1,2},{3,0},{3} } 有相同的金额。

4 个答案:

答案 0 :(得分:0)

如果我理解正确,那么您就是在限制问题,以至于您无法重新排列数组中值的顺序。假设问题集合理,您可以轻松地遍历并测试每个可能的变化。这将是递归的,但不需要动态编程。我面前没有IDE,但我相信代码看起来像这样(适应所选语言):

public int countWays(Array array) {
    int count = 0;
    int sum = 0;
    for (int index = 0; index < array.length(); ++index) {
        // how much would each partition sum to if we partitioned here?
        sum += array.get(index);

        // how many ways could the rest of the array be split to match that sum?
        count += recursiveCount(sum, array.subArray(index + 1, array.length());
    }

    return count;
}

public int recursiveCount(int partitionSum, Array remaining) {
    // base case
    if (remaining.length() == 0) {
        return 1;
    }

    int count = 0;
    int sum = 0;
    for (int index = 0; index < remaining.length(); ++index) {
        // how much would this partion sum to if we partitioned here?
        sum += remaining.get(index);

        // did we hit the mark?
        if (sum == partitionSum) {
            // make a partion here and see if we can finish the rest of the array
            count += recursiveCount(paritionSum, remaining.subArray(index + 1, remaining.length());
        }
    }
    return count;
}

*注意:这假设一个subArray函数,它从第一个索引包含到第二个索引,专门创建一个NEW数组。我相信这是大多数图书馆中最常见的行为。

您对输入的了解越多,您可以添加更多的快捷方式,以提高效率。例如;如果您知道数组只包含正数,那么当您的总和大于目标值时,您可以停止测试分区,因为无论您添加什么正数,它都不会将总和返回到目标值。

答案 1 :(得分:0)

在提出的问题中,我们需要在数组中找到三个连续的部分,它们的总和是相同的。我将提及步骤以及将为您解决问题的代码片段。

  1. 通过执行线性扫描O(n)和计算sum / 3来获取数组的总和。
  2. 从头开始扫描给定的数组。在每个索引处我们需要存储我们可以得到等于(sum / 3)的和的方式的数量,即如果end [i]是3,那么从索引i到n(数组范围)开始的数组中有3个子集其中sum是sum / 3。
  3. 第三步也是最后一步是从头开始扫描并找到sum为sum / 3的索引。在找到索引添加到解决方案变量(启动为零)时,结束[i + 2]。
  4. 我们正在做的事情是,从start开始遍历数组,直到len(array)-3。在找到总和,sum / 3,比如说索引i,我们有上半部分。

    现在,不关心下半部分并将解决方案变量(启动为零)添加到等于end [i + 2]的值。 end [i + 2]告诉我们从i + 2开始到结束的总方式,得到的数量等于第三部分的sum / 3.

    在这里,我们所做的是处理第一和第三部分,我们也处理了第二部分,它将默认等于sum / 3。我们的解决方案变量将是问题的最终答案。

    以下是代码片段,以便更好地理解上述算法:: -

    在这里,我们正在进行向后扫描,以存储从每个索引的末尾获得sum / 3的方式的数量。

    long long int *end = (long long int *)calloc(numbers, sizeof(long long int); 
    long long int temp = array[numbers-1];
    if(temp==sum/3){
        end[numbers-1] = 1; 
    }
    for(i=numbers-2;i>=0;i--){
        end[i] = end[i+1];
        temp += array[i];
        if(temp==sum/3){
            end[i]++;
        }
    }
    

    一旦我们有了结束数组,我们就会进行前向循环并得到我们的最终解决方案

    long long int solution = 0;
    temp = 0;
    for(i=0;i<numbers-2;i++){
        temp+= array[i];
        if(temp==sum/3){
            solution+=end[i+2];
        }
    }
    

    解决方案存储最终答案,即将数组拆分为三个具有相等总和的连续部分的方式。

答案 2 :(得分:0)

这是一个经过纠正的工作解决方案,其中有两个基于@Mattew Pape答案的示例。但这是尝试所有可能分区的蛮力方法。可以进一步优化。此方法的复杂度为O(N!)。

public class partitonArray {

    static int total = 0;
    public static int countWays(int [] arr) {
        int sum = 0;

        for (int index = 0; index < arr.length; ++index) {
            sum += arr[index];
            List<List<Integer>> lst = new ArrayList();
            List<Integer> l1 = new ArrayList();
            for(int j=0;j<=index;j++)
                l1.add(arr[j]);
            lst.add(l1);
            recursiveCount(sum,  Arrays.copyOfRange(arr, index + 1, arr.length), lst);
        }

        return total;
    }

    public static void recursiveCount(int partitionSum, int [] remaining, List<List<Integer>> lst) {
        // base case
        if (remaining.length == 0 && lst.size() > 1) {
            System.out.println("Result here = "+lst.toString());
            total++;
        }

        int sum = 0;
        List<Integer> l1 = new ArrayList();
        for (int index = 0; index < remaining.length; ++index) {
            sum += remaining[index];
            l1.add(remaining[index]);

            // did we hit the mark?
            if (sum == partitionSum) {
                // make a partion here and see if we can finish the rest of the array
                lst.add(l1);
                recursiveCount(partitionSum, Arrays.copyOfRange(remaining, index + 1, remaining.length), lst);
                lst.remove(l1);
            }
        }
    }

    public static void main(String [] args) {
        //int [] arr ={ 2, 4, 6,7, 7, 6, 3, 3, 3, 4, 3, 4,5, 4, 4, 3, 3, 1,4};
        int [] arr = {1, 2, 3, 0, 3};
        System.out.println(countWays(arr));
    }

}




**Output**:

Result here = [[1, 2], [3], [0, 3]]
Result here = [[1, 2], [3, 0], [3]]
2





/* 
  Result here `enter code here`= [[2, 4, 6, 7], [7, 6, 3, 3], [3, 4, 3, 4, 5], [4, 4, 3, 3, 1, 4]]
  Result here = [[2, 4, 6, 7, 7, 6, 3, 3], [3, 4, 3, 4, 5, 4, 4, 3, 3, 1, 4]]
  2
  */

答案 3 :(得分:0)

解决方法如下:

  1. 首先,我们应该检查数组所有元素的总和是否可被3整除。如果它不能被3整除,那么我们将无法将其分为三部分。 因此,我们将返回0
  2. 很明显,每个连续部分的每个部分的总和等于所有元素的总和除以3。因此,我们将其称为part_sum,即 Array_Sum / 3
  3. 我们必须创建一个数组 suffix_part_count [] ,如果i中元素的总和,其中 suffix_part_count [i] 等于 1 第-第n等于 Array_Sum / 3 ,否则为 0 。现在,我们将从最后一个索引计算suffix_part_count数组的累积总和。这是因为当我们找到任何前缀部分时,我们将与正确的后缀位置匹配,从而获得后缀部分组合数。
  4. 然后,对于初始数组 1…i 的每个前缀,其总和等于 Array_Sum / 3 ,我们需要添加到答案 suffix_part_count [i +2] 。现在为什么 suffix_part_count [i + 2]
    因为,当获得等于该部分值的任何前缀部分时,我们就知道suffix_part_count数组是否存在i + 2索引处的任何后缀部分。我们需要从i + 2索引的后缀数组中获取计数,因为我们需要有另一个中间部分,该部分肯定等于该部分的值。

例如: {1、2、3、0、3} 。让我们执行上述步骤。

  1. 该数组的总和为9,并且可以被3整除。因此,我们可以在此处制作三部分。
  2. 每个零件的值将为(9/3)= 3
  3. 现在,从数组的最后一个索引开始,我们将检查第ith个索引与最后一个索引的总和是否等于3。如果它是3,那么我们找到一个后缀位置,在这里我们可以拥有三部分中的一部分。我们将 1 添加到后缀计数数组的该位置。后缀计数数组将为: {0,0,0,1,1}
  4. 现在,我们将对后缀计数数组进行累加,以获取可以在特定位置制作的后缀组合部分的数量。最后的后缀计数数组将是: {2,2,2,2,1}
  5. 现在,我们将从原始数组开始,然后找到等于3的前缀部分。我们将在 2nd 索引中找到该部分。现在,从第二个索引开始,我们将进入第4个(前缀索引+ 2)索引以获取后缀部分的数量。如前所述,我们也将有一个中间部分。现在,答案将增加2。
  6. 最终答案将为 2 ,因为在索引2 之后没有前缀部分,该前缀部分等于 3

我的解决方案: https://github.com/setu1421/programming-revamp/blob/master/InterviewBit/Arrays/Partitions.cpp