最大连续子段和
最大连续子段和
• 给定长度为n的整数序列,a[1...n], 求[1,n]某个子区间[i,j]使得a[i]+…+a[j]和最大,或者求出最大的这个和。例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4]。
• 穷举法(3次for循环)
第1次for循环,遍历数组所有数字,即确定子段和的首个数字;
第2次for循环,遍历数个数字之后的所有数字,即确定字段和的最后一个数字;
第3次for循环,遍历首个数字与最后一个数字,对之间所有数字求和。
1 int maxSum(int a[], int num, int &start, int &end){
2 int localSum= MINNUM;
3 for (int i = 0; i < num; i++){
4 for (int j = i; j < num; j++){
5 int thisSum = 0;
6 for (int k = i; k <= j; k++){
7 thisSum += a[k];
8 }
9 if (thisSum > localSum){
10 start = i;
11 end = j;
12 localSum = thisSum;
13 }
14 }
15 }
16 return localSum;
17 }
• 穷举优化法(2次for循环)
第1次for循环,遍历数组所有数字,即确定子段和的首个数字;
第2次for循环,遍历数个数字之后的所有数字,即确定字段和的最后一个数字;
用thisSum记录不同结尾数字对应的子段和,进而比较获得对应首个数字的最大子段和
1 int maxSum(int a[], int num, int* start, int* end){
2 int localSum= 0;
3 for (int i = 0; i < num; i++){
4 int thisSum = 0;
5 for (int k = i; k < num; k++){
6 thisSum += a[k];
7 if (thisSum > localSum){
8 *start = i;
9 *end = k;
10 localSum = thisSum;
11 }
12 }
13 }
14 return localSum;
15 }
• 分治策略
• 将初始最大子段和问题分解为两个相同的子问题;
• 分别对相同的子段求解最大子段和;
• 合并子问题的解,获得原问题的解。
最大子段和位于左子段
最大子段和位于右子段
最大子段和的首数字位于左子段,尾数字位于右子段
1 int maxSum(int a[], int left, int right){
2 int localSum = 0;
3 if (left == right) localSum = (a[left] > 0 ? a[left] : 0);
4 else{
5 int mid = (left + right)/2;
6 int leftSum = maxSum(a, left, mid);
7 int rightSum = maxSum(a, mid + 1, right);
8 int sum1 = 0; int lefts = 0;
9 for (int i = mid; i >= left; i--){
10 lefts += a[i];
11 if (lefts > sum1) sum1 = lefts;
12 }
13 int sum2 = 0; int rights = 0;
14 for (int i = mid + 1; i <= right; i++){
15 rights += a[i];
16 if (rights > sum2) sum2 = rights;
17 }
18 localSum = sum1 + sum2;
19 if (localSum < leftSum) localSum = leftSum;
20 if (localSum < rightSum) localSum = rightSum;
21 } return localSum;
22 }
• 动态规划
令b[j]表示以位置 j 为终点的所有子区间中和最大的一个
子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中
如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含
如果b[j-1]<=0,那么b[j] = a[j] ,因为既然最大,前面的负数必然不能使你更大
1 int maxSum(int a[], int num)
2 {
3 int localSum = 0;
4 int b = 0;
5 for (int i = 0; i < num; i++)
6 {
7 if (b > 0)
8 b += a[i];
9 else
10 b = a[i];
11 if (b > localSum)
12 localSum = b;
13 }
14 return localSum;
15 }
• 算法效率分析
穷举法 O(n3)
穷举优化法 O(n2)
分治法 O(nlogn)
动态规划法 O(n)
• 附带一些完整代码:
1 //动态规划
2 # include<stdio.h>
3 # include<stdio.h>
4 int maxsum(int a[],int num)
5 {
6 int localsum=a[0];
7 int b=0;
8 for(int i=0;i<num;i++)
9 {
10 if(b>0)
11 b += a[i];
12 else
13 b=a[i];
14 if(b>localsum)
15 localsum=b;
16 }
17 return localsum;
18 }
19 int main()
20 {
21 int a[15];
22 int n,i;
23 while(scanf("%d",&n)!=EOF){
24 for(i=0;i<n;i++)
25 scanf("%d",&a[i]);
26 printf("%d\n",maxsum(a,n));
27 }
28 return 0;
29 }
30 //分治算法
31 # include<stdio.h>
32 int maxsum(int a[],int left,int right)
33 {
34 int localsum=0,i;
35 if(left==right)
36 localsum=(a[left]>0)?a[left]:0;
37 else{
38 int mid=(left+right)/2;
39 int leftsum=maxsum(a,left,mid);
40 int rightsum=maxsum(a,mid+1,right);
41 int sum1=0; int lefts=0;
42 for(i=mid;i>=left;i--){
43 lefts+=a[i];
44 if(lefts>sum1) sum1=lefts;
45 }
46 int sum2=0; int rights=0;
47 for(i=mid+1;i<=right;i++){
48 rights+=a[i];
49 if(rights>sum2) sum2=rights;
50 }
51 localsum=sum1+sum2;
52 if(localsum<leftsum) localsum=leftsum;
53 if(localsum<rightsum) localsum=rightsum;
54 }
55 return localsum;
56 }
57 int main()
58 {
59 int a[15];
60 int n,temp,i,j,k,max;
61 while(scanf("%d",&n)!=EOF){
62 for(i=0;i<n;i++)
63 scanf("%d",&a[i]);
64 printf("%d\n",maxsum(a,0,n-1));
65 }
66 return 0;
67 }
68
69 //暴力解法
70 # include<stdio.h>
71 int main()
72 {
73 int a[15];
74 int n,temp,i,j,k,max;
75 while(scanf("%d",&n)!=EOF){
76 for(i=0;i<n;i++)
77 scanf("%d",&a[i]);
78 max=0;
79 for(i=0;i<n;i++)
80 {
81 for(j=i;j<n;j++)
82 {
83 int temp=0;
84 for(k=i;k<=j;k++)
85 temp+=a[k];
86 if(temp>max)
87 max=temp;
88 }
89
90 }
91 printf("%d\n",max);
92 }
93 return 0;
94 }