最大子序列

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大子序列相关的知识,希望对你有一定的参考价值。

转自 http://blog.csdn.net/luxiaoxun/article/details/7438315

问题: 
给定一整数序列A1, A2,... An (可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大 
例如:整数序列-2, 11, -4, 13, -5, 2, -5, -3, 12, -9的最大子序列的和为21。对于这个问题,最简单也是最容易想到的那就是穷举所有子序列的方法。利用三重循环,依次求出所有子序列的和然后取最大的那个。当然算法复杂度会达到O(n^3)。

  1. int maxSubSum1(int a[],int size )   
  2. {  
  3.     int maxSum = 0;  
  4.     for ( int i = 0; i < size; i++ )  
  5.         for ( int j = 1; j < size; j++ )   
  6.         {  
  7.              int thisSum = 0;  
  8.              for ( int k = i; k <= j; k++ )  
  9.                  thisSum += a[k];  
  10.              if ( thisSum > maxSum )  
  11.                 maxSum = thisSum;  
  12.         }  
  13.     return maxSum;  
  14. }  

这个算法很简单,i表示子序列起始下标,j表示子序列结束下标,遍历子序列的开头和结束下标,计算子序列的和,然后判断最大子序列。很明显的看出算法复杂度是O( pow( n, 3 ) )

显然这种方法不是最优的,下面给出一个算法复杂度为O(n)的线性算法实现,算法的来源于Programming Pearls一书。在给出线性算法之前,先来看一个对穷举算法进行优化的算法,它的算法复杂度为O(n^2)。其实这个算法只是对对穷举算法稍微做了一些修改:其实子序列的和我们并不需要每次都重新计算一遍。假设Sum(i, j)是A[i] ... A[j]的和,那么Sum(i, j+1) = Sum(i, j) + A[j+1]。利用这一个递推,我们就可以得到下面这个算法: 

  1. int max_sub(int a[],int size)  
  2. {  
  3.   int i,j,v;  
  4.     int max=a[0];  
  5.   for(i=0;i<size;i++)  
  6.   {  
  7.     v=0;  
  8.     for(j=i;j<size;j++)  
  9.     {  
  10.       v=v+a[j];         //Sum(i, j+1) = Sum(i, j) + A[j+1]  
  11.       if(v>max)  max=v;  
  12.     }  
  13.   }  
  14.   return max;  
  15. }  

那怎样才能达到线性复杂度呢?这里运用动态规划的思想。先看一下源代码实现:

  1. int max_sub2(int a[], int size)  
  2. {  
  3.   int i,max=0,temp_sum=0;  
  4.   for(i=0;i<size;i++)  
  5.   {  
  6.       temp_sum+=a[i];  
  7.       if(temp_sum>max)  
  8.         max=temp_sum;  
  9.       else if(temp_sum<0)  
  10.         temp_sum=0;  
  11.   }  
  12.   return max;  
  13. }  

在这一遍扫描数组当中,从左到右记录当前子序列的和temp_sum,若这个和不断增加,那么最大子序列的和max也不断增加(不断更新max)。如果往前扫描中遇到负数,那么当前子序列的和将会减小。此时temp_sum 将会小于max,当然max也就不更新。如果temp_sum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时将temp_sum置为0。然后,temp_sum将从后面开始将这个子段进行分析,若有比当前max大的子段,继续更新max。这样一趟扫描结果也就出来了。 

全部代码:

 1 #include <stdio.h>
 2 
 3 int fun1(int a[], int n_size)
 4 {
 5     int max;
 6     int temp;
 7     int i, j, k;
 8     temp = 0;
 9     max = 0;
10     
11     for (i=0; i<n_size; i++)
12     {
13         for (j=i+1; j<n_size; j++)
14         {
15             temp = 0;
16             for(k=i; k<=j; k++)
17             {
18                 temp += a[k];
19                 if(temp >= max)
20                     max = temp;
21             }
22         }
23     }
24     
25     return max;
26 }
27 
28 int fun2(int a[], int n_size)
29 {
30     int i, j;
31     int v, max;
32     v = a[0];
33     max = a[0];
34     
35     for (i=0; i<n_size; i++)
36     {
37         for (j=i+1; j<n_size; j++)
38         {
39             v += a[j];
40             if (v > max)
41                 max = v;
42         }
43         v = 0;
44     }
45     return max;
46 }
47 
48 int fun3(int a[], int n_size)
49 {
50     int i;
51     int max, temp;
52     temp = 0;
53     max = 0;
54     
55     for (i=0; i<n_size; i++)
56     {
57         temp += a[i];
58         if (temp > max)
59             max = temp;
60         if(temp < 0)
61             temp = 0;
62     }
63     return max;
64 }
65 int main()
66 {
67     int a[5]={6,-1,5,4,-7};
68     printf("fun1():%d\n", fun1(a, 5));
69     printf("fun2():%d\n", fun2(a, 5));
70     printf("fun3():%d\n", fun3(a, 5));
71     return 0;
72 }

运行结果:

技术分享

以上是关于最大子序列的主要内容,如果未能解决你的问题,请参考以下文章

动态规划求最大子序列之和以及序列号

找到总和最大的递增子序列

最大连续子序列问题(tyvj1305)

子序列最大和

dp-最大连续子序列的和

求连续序列的最大子序列和