问题描述:
给定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:n]分解为长度相等的2段a[1:n/2],和a[n/2+1, n],分别求出这2个字段的和,与a[1:n]的最大字段和相比,有三种情况会出现:
1. a[1:n/2]与a[1:n]的最大子段和相同 <=> 最大字段在前半部分
2. a[n/2+1, n]与a[1:n]的最大子段和相同 <=> 最大子段在后半部分
3. a[1:n]的最大子段和为越过中点n/2,这种情况下a[n/2]和a[n/2+1]在最优子序列中,在[i,n/2]找到最大值S1,在[n/2+1,j]中找到最大值S1,所求最优值为S1+S2,最优序列为i->j。
源程序代码:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#define MAX 10010
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 mid;
mid = (right + left)/2 ;
int leftsum = MaxSubSum(a, left, mid);//求左段的最大子段和
int rightsum = MaxSubSum(a, mid+1, right);//求右段的最大子段和
/*第三种情况下的左方最大子段和*/
int s1 = 0;
int lefts = 0;
for(int i=mid; i>=left; i--)
{
lefts+=a[i];
if(lefts>s1)
s1 = lefts;
}
/*第三种情况下的右方最大子段和*/
int s2=0;
int rights=0;
for(int i=mid+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 num;
int sequence[MAX];
int ANS;
cout<<"输入序列的个数:";
cin>>num;
cout<<"请输入序列的各数字"<<endl;
for(int i=0; i<num; i++)
{
cin>>sequence[i];
}
cout<<MaxSubSum(sequence, 0, num-1)<<endl;
return 0;
}
动态规划算法:
根据分治法,分析左右子段s1,s2与原序列最优值之间的关系,需要知道段与子段之间最优值的递归关系。对所求的最大子段有如下关系:
二维表示的左端i,右端j转化为一维的表示b[j],左端j,利于降低算法复杂性。
根据b[j]的定义,他表示 [i,j] (1《 i《j )连续范围内序列和的最大值,有:
当b[j-1]>0时,b[j]=b[j-1]+a[j],
当b[j-1]<=0时,b[j] = a[j];
综上所述得到递归方程:
源程序代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define maxn 5000
using namespace std;
FILE *fp, *fp1;
int a[maxn], b[maxn];
int MaxSum(int n, int a[], int *l, int *r)
{
int sum = 0;
int bb=0;
*l = 0;
*r = 0;
for(int i=0; i<n; i++)
{
if(bb>0)
{
bb+=a[i];
}
else
{
bb=a[i];
*l=i;
}
if(bb>sum)
{
sum = bb;
*r = i;
}
}
return sum;
}
int main()
{
fp = fopen("附件2.最大子段和输入数据2017-序列1.txt","r");
fp1 = fopen("附件2.最大子段和输入数据2017-序列2.txt","r");
int count = 0, count1=0;
int l1=0, r1=0;
int l2=0, r2=0;
while(!feof(fp))
{
fscanf(fp, "%d", &a[count]);
count++;
}
while(!feof(fp1))
{
fscanf(fp1, "%d", &b[count1]);
count1++;
}
cout<<"序列1的最大子段和为:";
cout<<MaxSum(count, a, &l1, &r1);
cout<<"序号为"<<l1<<"至"<<r1<<endl;
cout<<"序列2的最大子段和为:";
cout<<MaxSum(count, b, &l2, &r2);
cout<<"序号为"<<l2<<"至"<<r2<<endl;
return 0;
}