代码改变世界

最大子段和 问题总结

2013-11-03 18:59 by youxin, ... 阅读, ... 评论, 收藏, 编辑

一维数组:  有n个数(以下都视为整数),每个数有正有负,现在要在n个数中选取相邻的一段,使其和最大,输出最大的和。

[a1,a2,a3................,an].

解法1:很自然的想法

int maxSum1(int a[],int n,int &besti,int &bestj)
{
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            int thissum=0;
            for(int k=i;k<=j;k++)
                thissum+=a[k];

            if(thissum>sum)
            {
                sum=thissum;
                besti=i;
                bestj=j;
            }
        }
    }
    return sum;
}

解法2:

我们可以将后面的一个for循环去掉:

int maxSum2(int a[],int n,int &besti,int &bestj)
{
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        int thissum=0;
        for(int j=i;j<=n;j++)
        {
              thissum+=a[j];

            if(thissum>sum)
            {
                sum=thissum;
                besti=i;
                bestj=j;
            }
        }
    }
    return sum;
}

 

解法3:分治算法:

int maxSubSum(int a[],int left,int right)
{
    int sum=0;
    if(left==right)
        sum=a[left]>0?a[left]:0;
    else
    {
        int center=(left+right)/2;
        int leftSum=maxSubSum(a,left,center);
        int rightSum=maxSubSum(a,center+1,right);

        int s1=0;
        int lefts=0;
        for(int i=center;i>=left;i--)
        {
            lefts +=a[i];
            if(lefts>s1)
                s1=lefts;
        }

        int s2=0;
        int rights=0;
        for(int i=center+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 maxSum3(int a[],int n)
{
    return maxSubSum(a,1,n);

}

解法4:动态规划

上面公式看不清楚,可以看下面的:

,则所求的最大子段和为:

 由b[j]的定义知,当b[j-1]>0时,b[j]=b[j-1]+a[j],否则b[j]=a[j]。由此可得b[j]的动态规划递推式如下:

     b[j]=max{b[j-1]+a[j],a[j]},1<=j<=n。

 

int maxSum4(int a[],int n)
{
    int sum=0;
    int b=0;
    for(int i=1;i<=n;i++)
    {
        if(b>0) b+=a[i];
        else    b=a[i];
        if(b>sum)
            sum=b;
    }
    return sum;
}

动态规划解释:

——动态规划。我们令一个数组f,f[i]表示前i个元素能组成的最大和。如果f[i-1]大于零,则不管a[i]的情况,f[i-1]都可以向正方向影响a[i],因此可以将a[i]加在f[i-1]上。如果f[i-1]小于零,则不管a[i]再大,都会产生负影响,因此我们还不如直接令f[i]=a[i]。因此状态转移方程就在这里。我们只需在f中扫描一次,找到最大的值就是最大子段的和。

int LSS_DP(int a[])                 //求最大子段和,动态规划,O(n)
{
int f[101], n = a[0], max = -200000000;           //f[i]表示第 i 个数能构成的最大和, max 表示当前所有中的最大和

f[1] = a[1];

for (int i = 2; i <= n; i++)
{
   if (f[i - 1] > 0)               //如果第 i 个数后面一个数能构成的最大子段和大于 0
   {
    f[i] = f[i - 1] + a[i];             //大于就将第 i 个数加入其中
   }
   else
    f[i] = a[i];               //否则第 i 个数自己组成一个最大子序列

   if (f[i] > max)                //更新最大值
    max = f[i];
}

return max;
}

 

还可以扩展该问题。

最大m子段和问题

     (1)问题描述:给定由n个整数(可能为负数)组成的序列a1,a2,a3……an,以及一个正整数m,要求确定此序列的m个不相交子段的总和达到最大。最大子段和问题是最大m字段和问题当m=1时的特殊情形。

     (2)问题分析:设b(i,j)表示数组a的前j项中i个子段和的最大值,且第i个子段含a[j](1<=i<=m,i<=j<=n),则所求的最优值显然为。与最大子段问题相似,计算b(i,j)的递归式为:

     其中,表示第i个子段含a[j-1],而项表示第i个子段仅含a[j]。初始时,b(0,j)=0,(1<=j<=n);b(i,0)=0,(1<=i<=m)。

更多:

http://blog.csdn.net/liufeng_king/article/details/8632430