标签:
1、很容易想到的算法,复杂度为o(n^2)
1 int MaxSegSum1(int n, int *ans) 2 { 3 int sum = 0; 4 5 for(int i = 1; i <= n; ++i) 6 { 7 int this_sum = 0; 8 for(int j = i; j <= n; ++j) 9 { 10 this_sum += ans[j]; 11 if(sum < this_sum) 12 { 13 sum = this_sum; 14 } 15 } 16 } 17 18 return sum; 19 }
2、采用分治法来计算最大子段和,如何将序列a[1:n]分为长度相等的两个序列a[1:n/2]和a[n/2+1:n],分别求出这两个序列
的最大子段和,则序列a[1:n]的最大子段和存在下面三种情况:
(1)a[1:n]与a[1:n/2]的最大子段和相同;
(2)a[1:n]与a[n/2+1:n]的最大子段和相同;
(3)a[1:n]的最大子段的左半部分在a[1:n/2]中,右半部分在a[n/2+1:n]中。
(1)和(2)可以递归求得,关键在第(3)中情况,此时,可求得左半部分的和s1=,然后在a[n/2+1:n]中求得s2=,这样s1+s2就是对应(3)中的最优值
1 int MaxSubSum(int *ans, int left, int right) 2 { 3 int sum = 0; 4 5 if(left == right) 6 { 7 sum = ans[left] > 0 ? ans[left] : 0; 8 } 9 else 10 { 11 int center = (left + right) / 2; 12 int left_sum = MaxSubSum(ans, left, center); 13 int right_sum = MaxSubSum(ans, center+1, right); 14 15 int s1 = 0; 16 int lefts = 0; 17 for(int i = center; i >= left; --i) 18 { 19 lefts += ans[i]; 20 if(s1 < lefts) 21 { 22 s1 = lefts; 23 } 24 } 25 26 int s2 = 0; 27 int rights = 0; 28 for(int i = center+1; i <= right; ++i) 29 { 30 rights += ans[i]; 31 if(s2 < rights) 32 { 33 s2 = rights; 34 } 35 } 36 37 sum = s1 + s2; 38 sum = max(sum, left_sum); 39 sum = max(sum, right_sum); 40 } 41 42 return sum; 43 } 44 45 int MaxSegSum2(int n, int *ans) 46 { 47 return MaxSubSum(ans, 1, n); 48 }
3、动态规划算法
从分治法中可以看到,若令b[j]=,即以第j个元素为最大子段中最后一个元素的最大子段和,则所要求的最大子段和为,由此可得到状态转移方程为
b[j] = max{b[j-1] + a[j], a[j]}
则可以写出代码如下
1 int MaxSegNum(int n, int *ans) 2 { 3 int b[N]; 4 int sum = 0; 5 b[0] = 0; 6 for(int i = 1; i <= n; i++) 7 { 8 if((b[i-1] + ans[i]) > ans[i]) 9 { 10 b[i] = b[i-1] + ans[i]; 11 } 12 else 13 { 14 b[i] = ans[i]; 15 } 16 if(sum < b[i]) 17 { 18 sum = b[i]; 19 } 20 } 21 22 return sum; 23 }
再看,b[i]的值只与b[i-1]和a[i]有关,空间复杂度可以优化,完全没必要存储每一个b[i]值,优化后的代码如下
1 int MaxSegNum(int n, int *ans) 2 { 3 int b = 0; 4 int sum = 0; 5 for(int i = 1; i <= n; i++) 6 { 7 if(b > 0) //(b[i-1] + ans[i]) > ans[i] meas b[i-1] > 0 8 { 9 b += ans[i]; 10 } 11 else 12 { 13 b = ans[i]; 14 } 15 16 if(b > sum) 17 { 18 sum = b; 19 } 20 } 21 22 return sum; 23 }
很明显,动态规划的复杂度为O(n)
4、引申推广
4.1 最大子矩阵和问题
用二维数组a[1:m][1:n]表示m行n列的整数矩阵,a[i1:i2][j1:j2]表示左上角为(i1,j1)和右下角为(i2,j2)的子数组,子数组的和可表示为
S(i1,i2,j1,j2)=
则最大子数组和即为,注意到
= = ,其中t(i1,i2)为 =
设b[j] = ,则t(i1, i2) =
则可以写出代码如下
1 int MatrixMaxSegSum(int m, int n, int (*ans)[N]) 2 { 3 int b[N]; 4 int sum = 0; 5 6 for(int i = 1; i <= m; ++i) 7 { 8 for(int k = 1; k <= n; ++k) 9 { 10 b[k] = 0; 11 } 12 13 for(int j = i; j <= m; ++j) 14 { 15 for(int k = 1; k <= n; ++k) 16 { 17 b[k] += ans[j][k]; 18 } 19 int max = MaxSegNum(n, b); 20 if(max > sum) 21 { 22 sum = max; 23 } 24 } 25 } 26 return sum; 27 }
算法的关键在于将二维转化为一维情形,m行n列的二维数组转为(n+1)*n/2个一维数组,每个数组有m个元素,最后求这些一维数组每个的最大子段和,这些和的最大值即为所求值
4.2 最大m子段和问题
最大子段和问题是最大m子段和问题m=1的情形
令b[i,j]表示前j项i个子段的最大和,并且最后一个段包含a[j],则可得到状态转移方程
b[i,j] = max{b[i-1,j] + a[j], b[i-1,t] + a[j](i-1<=t<j)}
可写出代码如下
1 int MaxMSegSum(int *ans, int n, int m) 2 { 3 if(n < m || m < 1) 4 { 5 return 0; 6 } 7 8 9 memset(b, 0, sizeof(b)); 10 11 for(int i = 1; i <= m; i++) 12 { 13 for(int j = i; j <= n-m+i; j++) 14 { 15 if(j > i) 16 { 17 b[i][j] = b[i-1][j] + ans[j]; 18 19 for(int k = i-1; k < j; ++k) 20 { 21 if(b[i][j] < (b[i-1][k] + ans[j])) 22 { 23 b[i][j] = b[i-1][k] + ans[j]; 24 } 25 } 26 } 27 else 28 { 29 b[i][j] = b[i-1][j-1] + ans[j]; 30 } 31 } 32 } 33 34 int sum = 0; 35 for(int i = m; i <= n; ++i) 36 { 37 if(sum < b[m][i]) 38 { 39 sum = b[m][i]; 40 } 41 } 42 43 return sum; 44 }
待续。。。。
标签:
原文地址:http://www.cnblogs.com/ygw0616/p/4532428.html