最大子段和

问题描述:

给定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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值