求数列的最大子段和
给定了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("\nMax=%d",sum);
return 0;
}