最大连续子序列之和

经典算法——求最大子序列和

比较经典的算法问题,能够很好的体现动态规划的实现,以一点画龙点睛大大精简了算法复杂度,且实现简单。本文中实现了4:

一般 maxSubSequenceSum0 O(n^3)

简单优化过的算法 maxSubSequenceSum1 O(n^2)

分治法优化的算法 maxSubSequenceSum2 O(n*log(n))

动态规划的算法 maxSubSequenceSum3 O(n)

#include <math.h>

#include "mymath.h"

/*
*
计算序列的某段子序列的和,maxSubSequenceSum0使用
*/
static int subSequenceSum(int a[], int left, int right)
{
    int i, sum = 0;
    for (i = left; i <= right; i++)
    {
        sum = sum + a[i];
    }
    return sum;
}

/*
*
三层遍历求子序列和的最大值,算法复杂度O(n^3)
*/
int maxSubSequenceSum0(int a[], int len)
{
    int i, j;
    int curSum; /*
当前序列和 */
    int maxSum; /*
最大序列和 */

    /*
初始化最大子序列和为序列第一个元素 */
    maxSum = a[0];

    /*
第一层循环定义子序列起始位置 */
    for (i = 0; i < len; i++)
    {
        /*
起始位置为i,初始化当前和为0 */
        curSum = 0;

        /*
第二层循环定义子序列结束位置 */
        for (j = i; j < len; j++)
        {
            /*
第三层循环在函数sumSubseqence中,计算子序列和 */
            curSum = subSequenceSum(a, i, j);

            /*
与最大子序列和比较,更新最大子序列和 */
            if (curSum > maxSum)
            {
                maxSum = curSum;
            }
        }
    }
    return maxSum;
}

/*
*
双层遍历求子序列和的最大值,算法复杂度O(n^2)
*/
int maxSubSequenceSum1(int a[], int len)
{
    int i, j;
    int curSum; /*
当前序列和 */
    int maxSum; /*
最大序列和 */

    /*
初始化最大子序列和为序列第一个元素 */
    maxSum = a[0];

    /*
外层循环定义子序列起始位置 */
    for (i = 0; i < len; i++)
    {
        /*
起始位置为i,初始化当前和为0 */
        curSum = 0;

        /*
内层循环定义子序列结束位置 */
        for (j = i; j < len; j++)
        {
            /*
计算子序列和,并与最大子序列和比较,更新最大子序列和 */
            curSum = curSum + a[j];

            /*
与最大子序列和比较,更新最大子序列和 */
            if (curSum > maxSum)
            {
                maxSum = curSum;
            }
        }
    }
    return maxSum;
}

/*
*
某段字序列中,含左边界元素的字序列和中的最大值,_maxSubSequenceSum2中使用
*/
static int _maxLeftBoderSubSequenceSum(int a[], int left, int right)
{
    int i;
    int sum = 0;
    int maxSum = a[left];
    for (i = left; i <= right; i++)
    {
        sum += a[i];
        if (sum > maxSum)
        {
            maxSum = sum;
        }
    }
    return maxSum;
}

/*
*
某段字序列中,含右边界元素的字序列和中的最大值,_maxSubSequenceSum2中使用
*/
static int _maxRightBoderSubSequenceSum(int a[], int left, int right)
{
    int i;
    int sum = 0;
    int maxSum = a[right];
    for (i = right; i >= left; i--)
    {
        sum += a[i];
        if (sum > maxSum)
        {
            maxSum = sum;
        }
    }
    return maxSum;
}

/*
*
求序列某段子序列中子序列和最大值
*/
static int _maxSubSequenceSum2(int a[], int left, int right)
{
    int center;
    int leftMaxSum;
    int rightMaxSum;
    int maxLeftBorderSum;
    int maxRightBorderSum;

    /*
递归终止条件 */
    if (left == right)
    {
        return a[left];
    }

    /*
分治法递归开始,取中点二分处理 */
    center = (left + right) >> 1; /* center = (left + right) / 2; */

    /*
递归求左右子序列段中最大子序列和 */
    leftMaxSum = _maxSubSequenceSum2(a, left, center);
    rightMaxSum = _maxSubSequenceSum2(a, center + 1, right);

    maxLeftBorderSum = _maxRightBoderSubSequenceSum(a, left, center);
    maxRightBorderSum = _maxLeftBoderSubSequenceSum(a, center + 1, right);

    /*
     *
二分后的最大值有三个:
     *    1
leftMaxSum,左段最大子序列和
     *    2
rightMaxSum,右段最大子序列和
     *    3
maxLeftBorderSum+maxRightBorderSum,左段最大含右边界子序列和最大值和右段最大含左边界子序列和最大值,二者之和
     *
这三者中的最大值即为分段前的最大子序列和
     *
     *
分治算法核心部分,解决分治后结果归并问题,具体分析:
     *   
这是对分段后的子序列的一种划分,有三种,只需分别求出各种的最大值然后在三者之间取一个最大值即可:
     *       1
、子序列全在左段,最大子序列和为leftMaxSum
     *       2
、子序列全在右段,最大子序列和为rightMaxSum
     *       3
、子序列跨左右段,最大字序列和为maxLeftBorderSum+maxRightBorderSum
     */
    return tmax(leftMaxSum, rightMaxSum, maxLeftBorderSum+maxRightBorderSum);
}

/*
*
分治法实现,算法复杂度O(n*log(n))
*
分:使用二分法进行分段
*
治:详细算法见_maxSubSequenceSum2内描述,简述为:
*   
全段最大子序列为以下三者中的最大值
*      
左段最大子序列和
*      
右段最大子序列和
*      
左段最大含右边界子序列和最大值和右段最大含左边界子序列和最大值之和
*/
int maxSubSequenceSum2(int a[], int len)
{
    return _maxSubSequenceSum2(a, 0, len - 1);
}

/*
*
动态规划实现,算法复杂度O(n)
*/
int maxSubSequenceSum3(int a[], int len)
{
    int i;
    int curSum; /*
当前序列和 */
    int maxSum; /*
最大序列和 */

    /*
初始化当前序列和为0 */
    curSum = 0;

    /*
初始化最大子序列和为序列第一个元素 */
    maxSum = a[0];

    /*
开始循环求子序列和 */
    for (i = 0; i < len; i++)
    {
        curSum = curSum + a[i];

        /*
与最大子序列和比较,更新最大子序列和 */
        if (curSum > maxSum)
        {
            maxSum = curSum;
        }

        /*
动态规划部分,舍弃当前和为负的子序列 */
        if (curSum < 0)
        {
            curSum = 0;
        }
    }
    return maxSum;
}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

下面是自己的一些总结:

对于第一种方法,可以返回相应的序列的开始序号和结束的序号;

#include <cmath>

#include <iostream>

using namespace std;

 

//########################################

int SubSequenceSum(int a[],int left,int right)

{

         int i, sum = 0;

         for(i=left;i<=right;i++)

         {

                   sum += a[i];

         }

         return sum;

}

 

int MaxSubSequenceSum(int a[],int len,int &start,int &end)

{

         int i, j;

         int curSum;   //当前子序列的和

         int maxSum; //最大子序列的和

         //int start,end;

        

         maxSum = a[0]; 

         start = end = 0;

        

         for(i=0;i<len;i++)

         {

                   curSum = 0;

                   for(j=i;j<len;j++)

                            curSum = SubSequenceSum(a,i,j);

                   if(curSum>maxSum)

                   {

                            maxSum = curSum;

                            start = i;

                            end = j;

                   }

         }

         return maxSum;

}

 

//@@@@@@@@@@@@@@@@@@@@@@@@@@@

int MaxSubSequenceSum1(int a[],int len,int &start,int &end)

{

         int i,j;

         int curSum;

         int maxSum;

 

         maxSum = a[0];

 

         for(i = 0;i<len;i++)

         {

                   curSum = 0;

                   for(j=i;j<len;j++)

                   {

                            curSum += a[j];

                            if(curSum>maxSum)

                            {

                                     maxSum = curSum;

                            }

                   }

         }

         return maxSum;

}

 

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

int _MaxLeftBoderSubSequenceSum(int a[],int left,int right)

{

         int i;

         int sum = 0;

         int maxSum = a[left];

         for(i=left;i<=right;i++)

         {

                   sum += a[i];

                   if(sum>maxSum)

                   {

                            maxSum = sum;

                   }

         }

         return maxSum;

}

 

int _MaxRightBoderSubSequenceSum(int a[],int left,int right)

{

         int i;

         int sum = 0;

         int maxSum = a[left];

         for(i=right;i>=left;i--)

         {

                   sum += a[i];

                   if(sum>maxSum)

                   {

                            maxSum = sum;

                   }

         }

         return maxSum;

}

 

int MaxSubSequenceSum3(int a[],int len)

{

         int i;

         int curSum;

         int maxSum;

         int start,end;

 

         start = end = 0;

         curSum = 0;

         maxSum = a[0];

 

         for(i=0;i<len;i++)

         {

                   curSum += a[i];

                   if(curSum>maxSum)

                   {

                            maxSum = curSum;

                            end = i;

                   }

                   //当子序列的和为负数时则清零

                   if(curSum<0)

                   {

                            curSum = 0;

                            start = i + 1;

                   }                

         }

         cout<<" "<<start<<","<<end<<endl;

         return maxSum;

}

 

int main ()

{

         int a[] = {1,2,-5,9,-4,11,-5,6,-7,8};

         int start,end;

         int max = MaxSubSequenceSum(a,sizeof(a)/sizeof(int),start,end);

         cout<<max<<":"<<start<<" "<<end<<endl;

         int max1 = MaxSubSequenceSum1(a,sizeof(a)/sizeof(int),start,end);

         cout<<max1<<":"<<start<<" "<<end<<endl;

         int max3 = MaxSubSequenceSum1(a,sizeof(a)/sizeof(int),start,end);

         cout<<max1<<":"<<start<<" "<<end<<endl;

         return 0;

}

最大连续子序列和问题是指给定一个有`n`(`n >= 1`)个整数的序列,求出其中最大连续子序列的和,规定一个序列的最大连续子序列和至少为 0。使用 C++ 解决该问题有多种方法,以下是两种常见的方法: ### 蛮力法 从开头直接累加,如果子序列的和小于或者等于 0 了,则这个元素之前的序列都不要,子序列从下一个元素开始重新求和。示例代码如下: ```cpp #include<iostream> using namespace std; int main() { // 用数组存放序列 int arr[] = { 2,3,-5,11,-4,13,-9 }; // 求出序列的长度 int length = sizeof(arr) / sizeof(arr[0]); // 存放最大子序列和 int sum = 0, maxsum = 0; for (int i = 0; i < length; i++) { sum = sum + arr[i]; if (sum <= 0) { sum = 0; } if (sum > maxsum) { maxsum = sum; } } cout << "最大子序列和为:" << maxsum; return 0; } ``` ### 分治法 分治法的思路是将问题划分成子问题,分别求解子问题,最后合并子问题的解得到原问题的解。具体步骤为:递归计算整个位于前半部分的最长连续子序列;递归计算整个位于后半部分的最长连续子序列;通过两个连续循环,计算从前半部分开始但是在后半部分结束的最长连续子序列的和;选择上述 3 个子问题中的最大值,作为整个问题的解。示例代码如下: ```cpp #include<bits/stdc++.h> using namespace std; // 三个数最大值 long Max3(long a, long b, long c) { return max(max(a, b), c); } // 求序列 a[s...t] 中最大连续子序列 long MaxSum(int a[], int s, int t) { long maxlsum, maxrsum, maxl = 0, maxr = 0; if (s == t) { // 序列只有 1 个元素 return max(a[s], 0); } int mid = (s + t) / 2; // 左最大子序列和 maxlsum = MaxSum(a, s, mid); // 右最大子序列和 maxrsum = MaxSum(a, mid + 1, t); long templsum = 0; // 求左边加上 mid 元素构成的序列和 for (int i = mid; i >= s; i--) { templsum += a[i]; if (templsum > maxl) { maxl = templsum; } } long temprsum = 0; // 求右边构成的序列和 for (int i = mid + 1; i <= t; i++) { temprsum += a[i]; if (temprsum > maxr) { maxr = temprsum; } } return Max3(maxlsum, maxrsum, maxl + maxr); } int main() { int a[] = {-2, 11, -4, 13, -5, -2}; int n = sizeof(a) / sizeof(*a); cout << "最大连续子序列和为" << MaxSum(a, 0, n - 1); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值