码迷,mamicode.com
首页 > 其他好文 > 详细

最大子段和问题

时间:2015-05-27 08:33:23      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:

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 }
View Code

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 }
View Code

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 }
View Code

再看,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 }
View Code

很明显,动态规划的复杂度为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 }
View Code

算法的关键在于将二维转化为一维情形,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 }
View Code

待续。。。。

最大子段和问题

标签:

原文地址:http://www.cnblogs.com/ygw0616/p/4532428.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!