最大m子段和问题:将一个整数序列a1、a2、a3……an分成m段,使其总和最大。

(m=1时是最大子段和问题)

存储设计:

int数组b[i][j]: 当序列a被分成i个子段时, 到第j项截至时的最大子段和。该问题最终的最优解为:max { b(m, j ), (m <= j <= n ) }

递归式:

    b(i, j) = max{ b(i, j-1)+a[j], max {b(i-1, t)+a[j], (1 <= t <= m )}} (1 <= i <=m; i <= j <= m )

其中:

b(i, j-1)+a[j]: 第i个子段包含a[j] ;

max {b(i-1, t)+a[j], (1 <= t <= m )}: a[j]独立作为新生成的子段。

代码:

int MaxSumM(int m, int n, int *a)
{
     int **b = new int*[m+1] ;
     for(int i=0; i <=m; i++) b[i] = new int[n+1];
     for(int i=0; i <=m; i++) b[0][i] = 0 ;
     for(int i=1; i <=m; i++)
     {
        b[i][i] = b[i-1][i-1]+a[i] ;
        for(int j=i+1; j <=n-m+i; j++)
        {
           b[i][j] = b[i][j-1]+a[j] ;
           for(int k=i-1; k<j; k++)
              if(b[i][j] < b[i-1][k]+a[j]) b[i][j] = b[i-1][k]+a[j] ;
         }
     }
 
     int sum=0 ;
     for(int i=m; i<=n; i++)
        if(sum < b[m][i]) sum = b[m][i];
     return sum ;
}

 

改进:

空间:实际上,数组b[][]每次只有第i-1行和第i行的值用到,因此只需存储b[][]的当前行即可。

时间:max {b(i-1, t)+a[j], (1 <= t <= m )}的值可以在计算第i-1行时预先存储起来,从而避免的重新计算。

代码:

int MaxSumM(int m, int n, int *a)
{
 int *b = new int[n+1] ;
 int *MAX = new int[n+1] ;
 b[0] = 0;
 int i, j ;
 for( i=0; i <=m; i++) MAX[i] = 0 ;
 for( i=1; i <=m; i++)
 {
  b[i] = b[i-1]+a[i] ;
  for(j=i+1; j <=n-m+i; j++)
  {
   if(MAX[j-1]<b[j-1])
   {
    b[j] = b[j-1]+a[j] ;
    MAX[j-1] = b[j-1] ;
   }
   else b[j] = MAX[j-1]+a[j] ;
   if(MAX[j-1]<MAX[j-2]) MAX[j-1] = MAX[j-2] ;
  }
  MAX[j-1] = (b[j-1]>MAX[j-2]) ? b[j-1]: MAX[j-2] ;
 }
 return MAX[n] ;
}