***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
第二章:动态规划
>最大子段和<
算法描述:
▪给定由n个整数(可能为负整数)组成的序列 a1,a2, ... , an ,求该序列形如 从ai 到 aj (i ≤ j)的子段和的最大值。当所有整数均为负整数时定义其最大值为0。根据这个定义,所求的最优值为:
max{ 0 ,ai到aj的和(1≤i≤j≤n)}
▪一个例子:
当有序列( a1,a2,a3,a4,a5,a6 )= (-2,11,-4,13,-5,-2)时,最大子段和为,a2+a3+a4=20
算法分析:
对于最大子段和问题,有多种求解算法。
> 首先来一个 最简单的算法
先用一个数组a[ ]来存储给定的n个整数,
类似于穷举法,把每种情况都求一遍,所以是三重循环,时间复杂度就是O(n^3)
代码可看下面 算法程序 的1-1
但是这也可以优化一下,因为 ai到aj的和 等于 aj 加上 ai到aj-1的和,所以这样就可以把最后一个循环省去,避免重复计算,从而使算法得以改进。
改进以后,这个算法只需要O(n^2)的计算时间。
具体代码可看下面 算法程序 的 1-2
> 然后就是用分治法解决
如果将所给的序列a[1:n]分为 长度相等的两段a[1:n/2] 和 a[n/2+1:n] ,分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种形式:
① a[1:n] 最大子段和与 a[1:n/2] 的最大子段和相同
② a[1:n] 最大子段和与 a[n/2+1:n] 的最大子段和相同
③ a[1:n] 最大子段和为 ai 到 aj 的和,且 1≤i≤n/2 ,n/2+1 ≤ j ≤ n
①和②两种情况可以递归求,重点来说一下 ③情况:
比较容易看出,a[n/2] 与 a[n/2+1] 在最优序列之中。因此我们可以在a[1:n/2]中计算出s1(s1等于ai到an/2的和,而 1≤i≤n/2 ),同理,可以在a[n/2+1:n]中计算出s2(ai到an的和,n/2+1≤i≤n)。
则s1+s2即为 ③时的情景。
具体代码,可看下面 算法程序 的2-1
该算法所需的计算时间T(n)满足典型的分治算法递归式:
T(n) = O(1) n≤c
2T(n/2)+O(n) n>c
此递归方程解得 T(n) = O(nlogn)
>最大子段和的动态规划算法
我们另外再声明一个数组 b[ ] ,
记 b[j] = 为 1~j 的 最大子段和的值 且 1≤j≤n
从 b[j] 的定义,可以发现,当 b[j-1]>0 时,b[j] = b[j-1] + a[j] ;
当 b[j-1] < 0 时, b[j] = a[j] 。
所以,b[j] 动态规划的递归式为:
b[j] = max{ b[j-1]+a[j] ,a[j] } 1 ≤ j ≤ n
再进行进一步优化,省略b[ ] 数组,
就像穷举法省略掉第三个循环一样,
此算法需要 O(n) 计算时间 和 O(n) 的计算空间。
具体代码,可见 算法程序 的 3-1
算法程序:
穷举法 1-1:
// 穷举法 O(n^3) int MaxSum( int n , int* a , int& besti, int& bestj ) { int sum = 0; for( int i = 0 ; i < n ; ++i ) { for( int j = i ; j < n ; ++j ) { int thissum = 0; for( int k = i ; k <= j ; ++k ) thissum += a[k]; if( thissum > sum ) { sum = thissum; besti = i; bestj = j; } } } return sum; }
1-2 穷举法优化:
// 1-2 穷举法的优化 O(n^2) int MaxSum( int n , int* a , int& besti , int& bestj ) { int sum = 0; for( int i = 0 ; i <= n ; ++i ) { int thissum = 0; for( int j = i ; j <= n ; ++j ) { thissum += a[j]; if( thissum > sum ) { sum = thissum; besti = i; bestj = j; } } } return sum; }
// 2-1 分治法 int MaxSubSum( int* a , int left , int right ) { int sum = 0; if( left == right ) sum = a[left]>0?a[left]:0; else { int center = (left+right)/2; int leftsum = MaxSubSum(a,left,center); int rightsum = MaxSubSum(a,center+1,right); int s1 = 0; int lefts = 0; for( int i = center ; i >= left ; --i ) { lefts += a[i]; if( lefts > s1 ) s1 = lefts; } int s2 = 0; int rights = 0; for( int i = center+1 ; i <= right ; ++i ) { rights += a[i]; if( rights > s2 ) s2 = rights; } sum = s1 + s2; if( sum < leftsum ) sum = leftsum; if( sum < rightsum ) sum = rightsum; } return sum; } int MaxSum( int n , int* a ) { return MaxSubSum(a,0,n-1); }
// 3-1 动态规划 int MaxSum( int n , int* a ) { int sum = 0, b = 0; for( int i = 0 ; i < n ; ++i ) { if( b > 0 ) b += a[i]; else b = a[i]; if( b > sum ) sum = b; } return sum; }
***************************************转载请注明出处:http://blog.csdn.net/lttree ********************************************