最大子段和问题

给定n个整数(可能为负整数)a1,a2,a3……an.求形如 ai,a(i+1),……,aj i,j=1,……n,i<=j
的子段和的最大值。当所有的整数均为负整数的时候定义其最大子段和为0,例如:当(a1,a2,a3,a4,a5,a6)=(-2,11,-4,13,-5,-2)时,最大子段和为 i=2,j=4(下标从1开始)。

一、枚举法解决

枚举所有可能的起始下标(i=1,2,3,……,n)和终止下标(j=i,i+1……,j),累加i到j所有元素的和,并从中选取最大的子段和。

partsum()
{
    int i,j,n,t,a[100];
    print("input number of data(<99):");
    input(n);
    print("input",n,"data:");
    for(i=1;i<=n;i++)
    {
        input(a[i]);
    }
    i=j=1;
    t=max_sum(a,n,&i,&j);
    print("The most sum of section is",t);
    print("starting point is",i);
    print("end point is",j);
}
max_sum(int a[],int n,int *best_i,int *best_j)
{
    int i,j,k,this_sum,sum;
    sum=0;
    *best_i=0;
    *best_j=0;
    for(i=1;i<=n;i++) 
    {
        for(j=1;j<=n;j++) 
        {
            this_sum=0;
            for(k=i;k<=j;k++) 
            {
                this_sum = this_sum+a[k];
            }
            if(this_sum>sum)
            {
                sum=this_sum;
                *best_i=i;
                *best_j=j;
            }
        }
    }
    return sum;
}

二、分治算法解决

如果将所给的序列a[1:n]分为长度相同的两段a[1:n/2]和a[(n/2)+1:n],分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种情形。
(1)a[1:n]的最大字段和与a[1:n/2]的最大字段和相同
(2)a[1:n]的最大字段和与a[(n/2)+1:n]的最大字段和相同
(3)a[1:n]的最大子段和为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;并计算出a[(n/2)+1:j]中的最大值s2.则s1+s2即为(3)的最优值。

partsum()
{
    int i,j,n,t,a[100];
    print("input number of data(<99):");
    input(n);
    print("input",n,"data:");
    for(i=1;i<=n;i++)
    {
        input(a[i]);
    }
    i=j=1;
    t=max_sum(a,n,&i,&j);
    print("The most sum of section is",t);
    print("starting point is",i);
    print("end point is",j);
}
int max_sum(int a[],int n)
{
    return max_sub_sum(a,1,n);
}
max_sub_sum(int a[],int left,int right)
{
    int center,i,j,left_sum,right_sum,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=max_sub_sum(a,left,center);
        right_sum=max_sub_sum(a,center+1,right);
        s1=0;//情形(3)的处理
        lefts=0;
        for(i=center;i>=left;i--) 
        {
            lefts=lefts+a[i];
            if(lefts>s1)
            {
                s1=lefts;
            }
        }
        s2=0;
        rights=0;
        for(i=center+1;i<=right;i++) 
        {
            rights=rights+a[i];
            if(rights>s2)
            {
                s2=rights;
            }
        }
        if(s1+s2<left_sum and right_sum<left_sum) 
        {
            return left_sum;
        }
        if(s1+s2<right_sum) 
        {
            return right_sum;
        }
        return s1+s2;
    }
}

三、动态规划算法解决

用动态规划法解决问题的思路很简单,就是通过开辟存储空间,存储各个子问题的计算结果,从而避免重复的计算。其实就是用空间效率去换取时间效率。
记sum[i]为a[1]~a[i]的最大字段和,记this_sum[i]为当前子段和。
this_sum[i]从i=1开始计算,当this_sum[i-1]>=0时,前面字段的和对总和有贡献,所以要累加当前元素的值;当this_sum[i-1]<0时,前面字段的和对总和没有贡献,要重新开始累加,以后的子段和从i开始。sum[i]在记录a[1]~a[i]的最大字段和,不断存储新得到的较大的this_sum[i]。
初值:this_sum[0]=0; i=1,2……n时
this_sum[i]=this_sum[i-1]+a[i] ——> 当this_sum[i-1]>=0
this_sum[i]=a[i] ——> 当this_sum[i-1]<0
相应地sum[i]的递推式如下:a[0]=0,i=1,2……n时,
sum[i]=sum[i-1] ——> 当this_sum[i]<=sum[i-1]
sum[i]=this_sum[i] ——> 当this_sum[i]>sum[i-1]

partsum()
{
    int i,j,n,t,a[100];
    print("input number of data(<99):");
    input(n);
    print("input",n,"data:");
    for(i=1;i<=n;i++)
    {
        input(a[i]);
    }
    i=j=1;
    t=max_sum(a,n,&i,&j);
    print("The most sum of section is",t);
    print("starting point is",i);
    print("end point is",j);
}
max_sub_sum(int a[],int n,int *best_i,int *best_j)
{
    int i,j,this_sum[n+1],sum[n+1];
    this_sum[0]=0;
    *best_i=0;
    *best_j=0;
    i=1;
    for(j=1;j<=n;j++) 
    {
        this_sum[j] = this_sum[j-1]+a[j];
        if(this_sum[j]>sum[j])
        {
            sum[j]=this_sum[j];
            *best_i=i;
            *best_j=j;
        }
        else if(this_sum[j]<0)
            {
                i=j+1;
                this_sum[j]=0;
            }
    }
    return sum;
}

四、递推算法解决

在上述算法的空间上进行优化,存储a[1]~a[j]的当前子段和的this_sum及当前最大子段和的sum都不必设置为n个元素的数组,用普通变量就可以实现了。因为在递推的过程中,只需保存一个值就足够了。

partsum()
{
    int i,j,n,t,a[100];
    print("input number of data(<99):");
    input(n);
    print("input",n,"data:");
    for(i=1;i<=n;i++)
    {
        input(a[i]);
    }
    i=j=1;
    t=max_sum(a,n,&i,&j);
    print("The most sum of section is",t);
    print("starting point is",i);
    print("end point is",j);
}
max_sub_sum(int a[],int n,int *best_i,int *best_j)
{
    int i,j,this_sum,sum;
    this_sum=0;
    *best_i=0;
    *best_j=0;
    i=1;
    for(j=1;j<=n;j++) 
    {
        this_sum = this_sum+a[j];
        if(this_sum>sum)
        {
            sum=this_sum;
            *best_i=i;
            *best_j=j;
        }
        else if(this_sum<0)
            {
                i=j+1;
                this_sum=0;
            }
    }
    return sum;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值