Algorithm 将数组划分为尽可能多的连续部分

Algorithm 将数组划分为尽可能多的连续部分,algorithm,Algorithm,给定一个正整数数组,在第一部分的和大于第二部分的和、第二部分的和大于第三部分的和的条件下,求连续的部分的最大数目,依此类推 我知道需要动态规划。我想到了计算所有可能的分区,然后将这个算法转换成一个记忆版本的蛮力 另一种方法是向后遍历数组,从最后一个元素作为一个分区开始,然后将元素相加,直到它们的和大于下一部分的和 任何建议都是非常受欢迎的。我最初打算采用贪婪算法,但实际上这里需要实现一个动态算法 从结尾开始似乎直觉上更容易。其主要思想是,对于递归中的给定步骤,我们有一组已建立的分区、一个正在进行

给定一个正整数数组,在第一部分的和大于第二部分的和、第二部分的和大于第三部分的和的条件下,求连续的部分的最大数目,依此类推

我知道需要动态规划。我想到了计算所有可能的分区,然后将这个算法转换成一个记忆版本的蛮力

另一种方法是向后遍历数组,从最后一个元素作为一个分区开始,然后将元素相加,直到它们的和大于下一部分的和


任何建议都是非常受欢迎的。

我最初打算采用贪婪算法,但实际上这里需要实现一个动态算法

从结尾开始似乎直觉上更容易。其主要思想是,对于递归中的给定步骤,我们有一组已建立的分区、一个正在进行的分区和序列的其余部分。然后,算法必须选择最合适的解决方案,将下一个元素(或上一个元素,如果向后,这就是我们在下面的代码中所做的)添加到当前分区,然后完成分区,或者在这里终止当前分区并完成分区(如果适用:当前分区可能未达到此步骤中要终止的最小和)

我们必须跟踪分区
p
、当前位置
i
、当前区段到目前为止的累计总和
s
,以及之前的总和
s'

以下是OCaml中可能的实现(代码未经测试):

让分区按增长求和=
设n=Array.length a in
让我们记录pbgs p s'i=
如果i<0,则1+List.length l,l::p
否则让x=a(i)进入
如果x+s>=s',则
设n1,l1=pbgs p(x+s)s’(pred i)
n2,l2=pbgs(i::p)0(x+s)(predi)in
如果n1>n2,则n1、l1、n2、l2
其他PBG p(x+s)s'(pred i)
在PBG[]0(pred n)中

对于记忆,您需要跟踪从每个位置向前(或向后)完成的最佳分区,但此分区取决于后面(或前面)分区所达到的总和这个位置,因此任何被记录的计算只会在导致这个位置的相同状态的分区中有用。我不确定这是否非常有效。

问题中建议的贪婪算法不起作用。你可能认为列表的最后一个元素确实应该是它自己的分区,但这里是反例:

[16,15,1,5,7]

如果将7作为一个分区,那么最终将得到2个分区。但是16,15,(1+5+7)是一个更好的解决方案

void getMaxPartitions (int[] A) {

   int len = A.length;
   int[] numP = new int[len+1];
   int[] lastSum = new int[len+1];
   int[] lastI = new int[len+1];

   numP[1] = 1;
   lastSum[1] = A[0];
   lastI[1] = 0;
   for (int i = 2; i <= len; i++) {
     int maxIndex = 0;
     int maxPs = 0;
     int maxSum = 0;
     for(int j = 1; j <= i; j++) {
       int sum = 0;
       for(int k = i-j; k < i; k++) {
         sum += A[k];
       }
       if(sum >= lastSum[i-j]) {
         if(maxPs < numP[i-j] + 1) {
           maxPs = numP[i-j]+1;
           maxSum = sum;
           maxIndex = i-j;
         }
       }
     }
     numP[i] = maxPs;
     lastSum[i] = maxSum;
     lastI[i] = maxIndex;
   }
   System.out.println("max partitions = " + numP[len]);
   int i = len;
   while (i > 0) {
     System.out.println(lastSum[i]);
     i = lastI[i];
   }
}
我建议你看一看

从最后一项开始,分支是选择分区的不同选择(最后一项为n),通过考虑>=规则检查部分解决方案的可行性,目标函数是最大分区数。一个好的边界函数是假设所有剩余项都是大小为1的分区


我的另一个建议是在颠倒列表后重新表述问题,这将更直观。颠倒列表并将规则更改为这是一个有趣的问题。它类似于原始分区问题,只是这里分区的总和需要以单调递增的顺序。动态规划电流关系如下所示:

numP[i] = max {numP[i-j] + 1} for those values of j (1<=j<=i) such that sum(A[i-j,i-j+1,...,i] >= lastSum[i-j])
        = 1 for i = 1

lastSum[i] = the sum of the last partition in numP[i] solution.
           = A[0] for i = 1;

lastI[i] = starting index of the last partition in numP[i] solution; // this is needed to obtain the partitions in the solution
针对以下输入测试程序,结果如下所示:

(1) {3,4,7,1,5,4,11}     max partitions = 5 {3,4,8,9,11}
(2) {1,2,3,4,5,6,7,11}   max partitions = 8 {1,2,3,4,5,6,7,11}
(3) {1,1,1,1,1,1,1,1,1}  max partitions = 9 {1,1,1,1,1,1,1,1,1}
(4) {9,8,7,6,5,4,3,2,1}  max partitions = 3 {9,15,21}
(5) {40,8,7,6,5,4,3,2,1} max partitions = 1 {76}

你的最后一个算法将如何处理(3,3,3,3,1,2)?
(1) {3,4,7,1,5,4,11}     max partitions = 5 {3,4,8,9,11}
(2) {1,2,3,4,5,6,7,11}   max partitions = 8 {1,2,3,4,5,6,7,11}
(3) {1,1,1,1,1,1,1,1,1}  max partitions = 9 {1,1,1,1,1,1,1,1,1}
(4) {9,8,7,6,5,4,3,2,1}  max partitions = 3 {9,15,21}
(5) {40,8,7,6,5,4,3,2,1} max partitions = 1 {76}