求数列的最大子段和
给定了n个元素的整数列(可能为负整数),a1,a2,a3,a4....an,求形如 ai,ai+1,ai+2....aj的子段,使其和为最大。当所有的整数位负整数时,定义其最大子段为0、
例如当(a1,a2,a3,a4,a5)=(-2,11,-4,13,-5,-2)时,其最大子段和为20.
分治算法求解:
分治算法中a[1:n]的最大子段和有三种情况 。a[1:n/2]和a[(n/2)+1:n]表示将数列a[1:n]等分成两段。
数列子段和最大的三种情况:
(1)最大子段和在a[1:n/2]中
(2)最大子段和在a[(n)/2+1:n]中
(3)最大子段和在a[i:j]当中,1<=i<=n/2<n/2+1<=j<=n,即最大子段的一部分在左边子段中,一部分在右边子段中
对于(1)和(2)两种情况,可通过递归求得。对于(3),则有a[n/2]和a[n/2+1]一定在最大子段中。因此可以算出a[i:n/2]的最大值s1和aa[n/2+1:j]的最大值s2.及s1+s1为情况(3)的最优解
代码如下
#include <iostream> using namespace std; int MaxSum(int a[],int left,int right) { int center,left_sum,right_sum,s1,s2,lefts,rights; if(left==right) { if(a[left]>0) return a[left]; else return 0; } else { center=(left+right)/2; left_sum=MaxSum(a,left,center);//计算情况(1)的最大值 right_sum=MaxSum(a,center+1,right);//计算情况(2)的最大值 s1=0; lefts=0; //从中间往两边计算 for(int i=center;i>=left;i--) { //计算左边的最大值 lefts+=a[i]; if(lefts>s1) s1=lefts; } s2=0; rights=0; for(int i=center+1;i<=right;i++) { //计算右边的最大值 rights+=a[i]; if(rights>s2) s2=rights; } if(s1+s2<left_sum) return left_sum; else if(s1+s2<right_sum) return right_sum; else return s1+s2; } } int main() { int a[20],n,Max; printf("输入元素个数:"); scanf("%d",&n); printf("输入元素:"); for(int i=1;i<=n;i++) scanf("%d",&a[i]); Max=MaxSum(a,1,n); printf("最大元素和为:%d",Max); return 0; }
动态规划算法
#include <iostream> using namespace std; #define NUM 100 int a[NUM]; int MaxSum(int n,int &besti,int &bestj) { int sum=0,b=0; int begin=0; for(int i=1;i<=n;i++) { if(b>0) b+=a[i]; else { b=a[i]; begin=i; } if(b>sum) { sum=b; besti=begin; bestj=i; } } return sum; } int main() { int n,sum,besti,bestj; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sum=MaxSum(n,besti,bestj); printf("最大子段为:"); for(int i=besti;i<=bestj;i++) printf("%d ",a[i]); printf(" Max=%d",sum); return 0; }