【问题标题】:Count the number of ways to divide array into 3 contiguous parts having equal sum计算将数组分成总和相等的 3 个连续部分的方法数
【发布时间】:2015-03-11 19:59:25
【问题描述】:

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

【问题讨论】:

  • 这个问题似乎是题外话,因为它表明没有努力,不包含代码,甚至没有提出任何问题。
  • @RaymondChen 好吧,我确实修改了分区问题,但不擅长动态编程,代码似乎没用。

标签: arrays partition


【解决方案1】:

如果我对您的理解正确,那么您就是在限制问题,以至于您无法重新排列数组中值的顺序。假设问题集是合理的,您可以轻松循环并测试每个可能的变化。这将是递归的,但不需要动态编程。我面前没有 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 函数创建一个新数组,从第一个索引包含到第二个索引。我相信这是大多数库中最常见的行为。

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

【讨论】:

    【解决方案2】:

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

    1. 通过线性扫描 O(n) 得到数组的总和并计算 sum/3。
    2. 从末尾开始扫描给定的数组。在每个索引处,我们需要存储我们可以获得等于 (sum/3) 的总和的方式数量,即如果 end[i] 为 3,则数组中有 3 个子集,从索引 i 到 n(数组范围)其中 sum 是 sum/3。
    3. 第三步也是最后一步是从头开始扫描并找到 sum 为 sum/3 的索引。找到索引后添加到解决方案变量(初始化为零),end[i+2]。

    我们这里要做的是,从 start 开始遍历数组直到 len(array)-3。在假设索引 i 上找到总和 sum/3 后,我们得到了我们需要的前半部分。

    现在,不要关心后半部分,向解决方案变量(初始化为零)添加一个等于 end[i+2] 的值。 end[i+2] 告诉我们从 i+2 到结束的总路数,第三部分的总和等于 sum/3。

    在这里,我们已经处理了第一部分和第三部分,我们还处理了第二部分,默认情况下等于 sum/3。我们的解决方案变量将是问题的最终答案。

    为了更好地理解上述算法,下面给出了sn-ps代码::-

    这里我们进行反向扫描,以存储每个索引从末尾得到 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];
        }
    }
    

    solution 存储最终答案,即将数组拆分为三个总和相等的连续部分的方法数。

    【讨论】:

      【解决方案3】:

      这是一个更正的工作解决方案,有两个基于@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
        */
      

      【讨论】:

        【解决方案4】:

        解决方法如下:

        1. 首先,我们应该检查数组中所有元素的总和是否可以被3整除。如果它不能被 3 整除,那么我们将无法将它分成三个部分。 所以,我们将返回 0
        2. 很明显,每个连续部分的每个部分的总和将等于所有元素的总和除以3。因此,我们将其称为part_sum,即Array_Sum / 3
        3. 我们必须创建一个数组 suffix_part_count[],其中 suffix_part_count[i] 等于 1,如果 i 中的元素总和-th 到 n-th 等于 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. 现在,从数组的最后一个索引开始,我们将检查第 i 个索引到最后一个索引的总和是否等于 3。如果是 3,那么我们找到了一个后缀位置,我们可以在这三个部分中拥有一个部分。我们将 1 添加到后缀计数数组的该位置。后缀计数数组将为:{0, 0, 0, 1, 1}
        4. 现在,我们将对后缀计数数组进行累加求和,以获得在特定位置可以进行组合后缀部分的数量。最终的后缀计数数组将是:{2, 2, 2, 2, 1}
        5. 现在,我们将从原始数组开始,找到等于 3 的前缀部分。我们将在 2nd 索引中找到它。现在,从第二个索引开始,我们将转到 4th (prefix index + 2) 索引来表示后缀部分的数量。正如我们之前所说,我们也会有一个中间部分。现在,答案将增加 2。
        6. 最终答案将是 2,因为在 index 2 之后没有前缀部分,它等于 3

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

        【讨论】:

          猜你喜欢
          • 2012-01-07
          • 2020-09-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-19
          • 1970-01-01
          相关资源
          最近更新 更多