思路来自于微软的《编程之美》。注意:子序列(subsequence)和连续子数组还是不一样的,子序列可以是不连续的。

/*****************************

列出了四种算法,复杂度从O(N^3)到O(N)

Author: MicroGrape

Date:2009-5-20

****************************
*/


#include
<iostream>

using namespace std;
/*

O(N^3) algorithm
*/
int MaxSubSumA(int a[], int n)
{
    
int maxsum = 0
;

    
for(int i = 0; i < n ; i++
)
      {
        
for(int j = i ; j < n ; j++
)
          {
            
int sum = 0
;
            
for(int k = i ; k <= j ; k++
)
                  sum
+=
a[k];
            
if(sum >
maxsum)
                  maxsum
=
sum;
          }
      }
    
return
maxsum;
}

/*

O(N^2) algorithm
充分利用了中间数据。
例如上一轮从a[4]加到a[8],下一轮从a[4]加到a[9]时。
实际上可以利用上面的结果,减少重复计算。
即:以a[i]起头的最大和子序列只可能有一个。
*/
int MaxSubSumB(int a[], int n)
{
    
int maxsum = 0
;

    
for(int i = 0; i < n ; i++
)
      {
        
int sum = 0
;
        
for(int j = i ; j < n ; j++
)
          {
              sum
+=
a[j];
            
if(sum >
maxsum)
                  maxsum
=
sum;
          }
      }
    
return
maxsum;
}

/*

O(N*logN) recursive algorithm
采用了分治和递归的思想。将一个数组分成两个数组处理。
*/
int MaxSubSumC(int a[], int left, int right)
{

    
//如果某边剩一个元素,如果大于0则累加,小于0则放弃

    if(left == right)
        
if(a[left] > 0
)
            
return
a[left];
        
else

            
return 0;

    
int middle = left + (right -left)/2
;
    
//左半边中子序列的最大和

    int leftsum = MaxSubSumC(a, left, middle);
    
//右半边中子序列的最大和

    int rightsum = MaxSubSumC(a, middle+1, right);

    
//跨两边子序列的最大和,所以从middle往两边起算

    int leftrightsum = 0;
    
//首先middle往左。一个标记最大值,一个标记临时值。

    int middleleftsum = 0, maxmiddleleftsum = 0;
    
for(int i = middle ; i >= left ; i--
)
      {
          middleleftsum
+=
a[i];
        
if(middleleftsum >
maxmiddleleftsum)
              maxmiddleleftsum
=
middleleftsum;
      }
    
//然后middle往右

    int middlerightsum = 0, maxmiddlerightsum = 0;
    
for(int j = middle+1 ; j <= right ; j++
)
      {
          middlerightsum
+=
a[j];
        
if(middlerightsum >
maxmiddlerightsum)
              maxmiddlerightsum
=
middlerightsum;
      }
      leftrightsum
= maxmiddleleftsum +
maxmiddlerightsum;

    
//取三者中的最大值

    return max(leftsum, max(rightsum, leftrightsum) );
}
//为了保持统一接口,给C写了一个驱动程序

int MaxSubSumC(int a[], int n)
{
    
return MaxSubSumC(a, 0, n-1
);
}


/*

O(N) algorithm
一个以a[0]开头的到a[i]子序列只有三种情况:
1.为负。则抛弃这个结果,从a[i+1]开始重新计算(减少了计算量)//else if
2.大于已知的最大和,记录下来。//if
3.最大和还未出现,继续累加下去。//未被以上两者匹配。
*/
int MaxSubSumD(int a[], int n)
{
    
int maxsum = 0, sum = 0
;
    
for(int i = 0; i < n ; i++
)
      {
          sum
+=
a[i];
        
if(sum >
maxsum)
              maxsum
=
sum;
        
else if (sum < 0
)
              sum
= 0
;
      }
    
return
maxsum;
}

附注一:最后一种算法的思想很巧妙,还有几点很值得说的。

比如

1)代码是如何使用一个sum值和maxsum值,实际上就给每一个数组的元素保存了一份信息。

2)通过上面一点,我们知道,最大和的结尾肯定是可以被记录下来的,因为每一个最大和都被记录了。但是怎么保证开头也是符合最大和要求的呢?其实最大和序列的起始要求只有一个:不能为负。所以里面的else if就保证了这一点。


posted on 2011-05-12 01:24  微型葡萄  阅读(557)  评论(0编辑  收藏  举报