jake1036

求最大连续子向量最大和

                                 最大连续子向量和

   一个整形数组,求其间连续的子向量,使其子向量的和最大。 如一组数组元素为 31 , -41 ,59 , 26 ,-53 ,58 , 97 , -93 , -23 , 84
      【2 , 6】之间的子向量之和最大。定义若数组中的元素全部为负数,则最大和值为0 。 若数组中的数字全部为正数,则最大和值即为数组中的全部元素之和。


        解法一:
        经过建模转换,即是求在[1,n]中 求[i,j]之和的最大值,使用暴力解法,算出任意[i,j]之间的各个值,然后取最大值,即为所求。
         

#include <iostream>
using namespace std ;
 
  
int sumMax(int *a , int n)
  
{
        
        
int max = 0 ,sum = 0 ; //
        for(int i = 0 ; i < n ; i++)
        
{
          sum 
= 0 ;      
          
for(int j = i ; j < n ; j++)
           
{
               sum 
+= a[j] ;
               
if(max < sum)
                
{
                  max 
= sum ;      
                }
     
           }

        }

      
      
return max ;
  }

 
 
  
int main()
  
{
    
int a[] = {31 , -41 ,59 , 26 ,-53 ,58 , 97 , -93-23 , 84} ;
    cout
<<"max"<<sumMax(a , 8) ;
    cin.
get() ;
   
    
return 0 ;    
  }

      


  解法二 
     第一种解法比较常见,但是此种解法则不常用。定义个数据S ,则S[i] 表示数组a[0]到a[i]之间的元素之和,那么S[j]-S[i] 的差值,表示
     a[i]到a[j]之间和。  本题的问题,就是求S[j]-S[i]的最大值。
   

#include <iostream>
 
using namespace std ;
 
 
int sumMax(int * s , int n )
 
{
     
int sum = 0 , max = 0 ;
     
for(int i = 0 ; i < n ; i++)
      
{        
        
for(int j = i + 1;j < n  ;j++
         
{
            sum 
= s[j] - s[i] ;
            
if(sum > max)
              max 
= sum ;        
         }

      }

      
return max ;
     
 }

 
 
int main()
 
{
     
   
int a[] = {31 , -41 ,59 , 26 ,-53 ,58 , 97 , -93-23 , 84} ;
   
int * s = new int[8];
   
int sum = 0 ;
   
for(int i = 0 ; i < 8 ; i++)
    
{
       sum 
+= a[i] ;    
       s[i] 
= sum ;
    }
 
     
     
     cout
<<"max"<<sumMax(s , 8);
     
     
   delete [] s ;
   cin.
get() ; 
   
return 0 ;    
 }
 


   综上,以上的两种解法都是O(n*n)级别的,实质上可以使用分治法来解决此问题。

  解法三:
   利用分治法解决最大子序列和问题
  可以将原数组从中间元素开始分成两个部分,左边部分最大子序列和为Ml,右边部分最大子序列和为Mr,还有一种情况即整个数组的
   最大子序列和存在左右两个部分之间,记为Mc。 所求结果即为Ml 、Mr、Mc 三者的最大值。利用递归算法解决此问题。


  

/*
 该算法使用了分治法,求最大子序列的和问题 
*/


#include 
<iostream>
 
using namespace std ;
 
 inline 
int Max(int , int) ;
 
int sumMax(int * a , int l , int r);

 
 
 
int main()
 
{
  
int a[] = {31 , -41 ,59 , 26 ,-53 ,58 , 97 , -93-23 , 84} ;
  cout
<<sumMax(a , 0 , 9) ; 
  cin.
get() ;
   
return 0 ;    
 }

 
 
 
 inline 
int Max(int x , int y)
 
{
    
return x>? x : y ;       
        
 }
 
 
 
  
int sumMax(int * a , int l , int r)
 
{
     
if(l > r)
       
return 0 ;
     
if(l == r)  
      
return Max(0 , a[l]) ;
     
//计算左部分的最大值
     
     
int m = (l + r) >> 1 ;
     
     
int lmax = 0 , sum = 0 ;
     
for(int i = m ; i >= 0 ; i--//左部分从中间向左依次叠加,然后判断是否获得最大值 
      
       sum 
+= a[i] ;
       lmax 
= Max(sum , lmax) ;  //左边最大值    
       }

       
      
int rmax = 0 ;
       sum 
= 0 ;
      
for(int j =  m + 1; j <= r ; j++)//右部分从中间向右依次叠加,然后判断是否获得最大值
        {
         sum 
+= a[j] ;
         rmax 
= Max(sum , rmax) ;  //右边最大值
        }
    
      
return Max(lmax + rmax , Max(sumMax(a , l , m) , sumMax(a , m + 1 , r) )) ;
       
     
 }



 第三个算法的时间复杂度为o(n *  log(n)) ,通过这个题目来学习分治法。




  第四种解法:
       该解法为一线性解法,算法复杂度为O(n) ,定义maxEndingHere为截止到a[j]处的最大子数组之和,则a[j+1]处的最大子数组之和为
      max(maxEndingHere + a[j + 1] , 0) ,而我们的所求maxSofar即为各个位置最大的maxEndingHere。

    

/*
 通过线性算法解决问题 
*/


#include 
<iostream>
 
using namespace std ;
 inline 
int Max(int , int) ;
 
int sumMax(int a[] , int n) ; 
 
 
int main()
 
{
   
int a[] = {31 , -41 ,59 , 26 ,-53 ,58 , 97 , -93-23 , 84} ;
  cout
<<sumMax(a  , 9) ; 
  cin.
get() ;
   
return 0 ;    
 }
 

 inline 
int Max(int x , int y)
 
{
    
return x>? x : y ;       
        
 }
 
 
  
int sumMax(int a[] , int n) 
      
{
        
int maxEndingHere = 0 , maxSoFar = 0 ; //maxEndingHere 表示截止到位置i处的最大子序列和 
        for(int i = 0 ; i < n ; i++
         
{
           maxEndingHere 
= Max(maxEndingHere + a[i] , 0 ) ; 
           maxSoFar 
= Max(maxEndingHere , maxSoFar) ;      
         }

         
         
return maxSoFar ;          
      }


    






          
   

posted on 2011-03-12 15:43 kahn 阅读(986) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理