给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-20,11,-4,13,-5,-2)时,最大子段和为20。
最大子段和是动态规划中的一种。
我们用枚举、分治及动态规划来实现最大字段和问题。
例:给定n个整数(可能为负数)组成的序列X,求该序列如x[i]+x[i+1]+…+x[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,x[i]+x[i+1]+…+x[j]},1<=i<=j<=n
输入:序列X
输出:最大子段和
枚举法:
#include<stdio.h>
#include<stdlib.h>
int MaxSum(int a[],int n){
int sum=0;
int i,j;
int besti,bestj;
for(i=1;i<=n;i++){
int thissum=0;
for(int j=i;j<=n;j++){
thissum +=a[j];
if(thissum>sum){
sum=thissum;
besti=i;
bestj=j;
}
}
printf("A[%d..%d]=%d",besti,bestj,sum);
}
int main(){
int A[15]={-15,32,-4,22,10,5,-9,20,-18,-11,2,23,-1,-17,28};
int i;
for(i=0;i<15;i++){
printf("%d",A[i]);
}
printf("\n");
MaxSum(A,15);
return 0;
}
分治法:
#include<iostream>
using namespace std;
int MaxSubSum(int a[],int left,int right){
int sum=0;
if(left==right)sum=a[left]>0?a[left]:0;
else{
int center=(left+right)/2;
int leftsum=MaxSubSum(a,left,center);
int rightsum=MaxSubSum(a,center+1,right);
int s1=0;
int lefts=0;
for(int i=center;i>=left;i--){
lefts+=a[i];
if(lefts>s1)s1=lefts;
}
int s2=0;
int rights=0;
for(int i=center+1;i<=right;i++){
rights+=a[i];
if(rights>s2)s2=rights;
}
sum=s1+s2;
if(sum<leftsum)sum=leftsum;
if(sum<rightsum)sum=rightsum;
}
return sum;
}
int main(){
int n,a[100],m,maxsum;
cout<<"请输入整数序列的元素个数n:"<<endl;
cin>>n;
cout<<"请输入序列中各元素的值a[i](一共"<<n<<"个)"<<endl;
for(m=0;m<n;m++)
cin>>a[m];
int b[100];
for(m=0;m<n;m++)
b[m+1]=a[m];
maxsum=MaxSubSum(b,1,n);
cout<<"整数序列的最大子段和是:"<<maxsum<<endl;
system("pause");
}
动态规划:
#include<stdio.h>
#include<stdlib.h>
int MaxSum(int a[],int n)
{
int sum=0,b=0;
int besti,bestj;
int i,c;
for(i=1;i<=n;i++)
{
if(b>0)
b+=a[i];
else{
b=a[i];
c=i;
}
if(b>sum){
sum=b;
besti=c;
bestj=i;
}
}
printf("A[%d..%d]=%d",besti,bestj,sum);
}
int main(){
int A[15]={-15,32,-4,22,10,5,-9,20,-18,-11,2,23,-1,-17,28};
int i;
for(i=0;i<15;i++){
printf("%d",A[i]);
}
printf("\n");
MaxSum(A,15);
return 0;
}