最大连续子段和
• 给定长度为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 }