最大子段和

来源:互联网 发布:ithink系统动力学软件 编辑:程序博客网 时间:2024/05/17 23:51

问题描述:

给定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 10010using 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 5000using 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;}