zoukankan      html  css  js  c++  java
  • 子序列最大和

    问题描述:

    x[i]中,从任意一个数x[a]到x[b](a<=b)的连续的序列叫做x[i]的子序列,

    x[i]的子序列的和叫做x[i]的子序列的序列和。

    现在已知序列x[i],求一些关于最大序列和的问题答案。

    一、子序列最大和

    【方法一】

    朴素的查找,利用S[i]=S[0]+…+S[i]

    进行一次O(n^2)的查找,并比较S[i]-S[j](i>j)

    求得最大值,即为答案

    【代码】

    【适用范围】

    需要直观地了解最大序列和的起止位置

    【方法二】见补充【需要求序列位置】【分治】

    【方法三】【不可以求序列位置】【动态规划】

    【数据结构】

    int lmax[MAXN]

    【代码】

    lmax[0] = x[0];

    for (int i=1; i<n; i++)

    if (lmax[i-1]<=0)

    lmax[i]=x[i];

    else

    lmax[i]=lmax[i-1] + x[i];

    /*

    * 在已知lmax[i-1]为包含x[i-1]的最大子序列和的条件下,

    * 如果lmax[i-1]为负数,则x[i]必大于lmax[i-1],故lmax[i]=x[i]

    * 如果lmax[i-1]为非负数,则x[i]+lmax[i-1]必大于x[i]或其他到x[i]为止的序列,故lmax[i]=x[i]+lmax[i-1]

    */

    二、双子序列最大和

    “双”这个字很奇特,见到“双”,那肯定是有奇技淫术可以使用。

    注意,这里的双,意思是两个子序列之间至少有1个单位长度的间隔。

    那么我们很自然地想到了这样一个场景:

    没错,普通的算法肯定能写,但复杂度一定不低,

    所以如果想要降低复杂度,程序的突破口就是在上图的缺口处。

    设缺口长度为1且位置为k

    令图中左边的序列为S_left=∑x[i] (i=1..k-1)

    令右边的序列为S_right=∑〖x[i](i=k+1..n)S

    S_left 中的最大子序列和与S_right 中的最大子序列和的和 就是双子序列最大和

    现在所要解决的问题是,引入一对新的量,lf[i]与lr[i],分别表示从最左、最右到i的子序列中的最大序列和。

    在得知lmax(与rmax)的基础上,我们可以很轻松地获取lf与lr,只需扫描一遍即可。

    出于节约空间的考虑,我们继续用lmax和rmax来表示lf和lr

    【代码】

     1 lmax[0] = x[0];
     2 
     3 for (int i=1; i<n; i++)
     4 
     5 if (lmax[i-1]<=0)
     6 
     7 lmax[i]=x[i];
     8 
     9 else
    10 
    11 lmax[i]=lmax[i-1] + x[i]; 
    12 
    13 /*此部分同上*/
    14 
    15 for (int i=1; i<n; i++)
    16 
    17 if (lmax[i]<lmax[i-1])
    18 
    19 lmax[i]=lmax[i-1];
    20 
    21 /*右边同理*/
    22 
    23 rmax[n-1]=rmax[n-2];
    24 
    25 for (int i=n-1; i>=0; i--)
    26 
    27 if (rmax[i+1]<=0)
    28 
    29 rmax[i]=x[i];
    30 
    31 else
    32 
    33 rmax[i]=rmax[i+1] + x[i];
    34 
    35 for (int i=n-1; i>=0; i--)
    36 
    37 if (rmax[i]<rmax[i+1])
    38 
    39 rmax[i]=rmax[i+1];
    40 
    41  
    View Code


    补充

    对这个问题,有一个相对复杂的O(NlogN)的解法,就是使用递归。如果要是求出序列的位置的话,这将是最好的算法了(因为我们后面还会有个O(N)的算法,但是不能求出最大子序列的位置)。该方法我们采用“分治策略”(divide-and-conquer)。

    在我们例子中,最大子序列可能在三个地方出现,或者在左半部,或者在右半部,或者跨越输入数据的中部而占据左右两部分。前两种情况递归求解,第三种情况的最大和可以通过求出前半部分最大和(包含前半部分最后一个元素)以及后半部分最大和(包含后半部分的第一个元素)相加而得到。

    //递归法,复杂度是O(nlogn) 

    long maxSumRec(const vector<int>& a, int left, int right) 

    {

    if (left == right) 

    if (a[left] > 0) 

    return a[left]; 

    else 

    return 0; 

    int center = (left + right) / 2; 

    long maxLeftSum = maxSumRec(a, left, center); 

    long maxRightSum = maxSumRec(a, center+1, right); 

    //求出以左边对后一个数字结尾的序列最大值

    long maxLeftBorderSum = 0, leftBorderSum = 0; 

    for (int i = center; i >= left; i--) 

    leftBorderSum += a[i]; 

    if (leftBorderSum > maxLeftBorderSum) 

    maxLeftBorderSum = leftBorderSum; 

    //求出以右边对后一个数字结尾的序列最大值

    long maxRightBorderSum = 0, rightBorderSum = 0; 

    for (int j = center+1; j <= right; j++) 

    rightBorderSum += a[j]; 

    if (rightBorderSum > maxRightBorderSum) 

    maxRightBorderSum = rightBorderSum; 

    return max3(maxLeftSum, maxRightSum,  

    maxLeftBorderSum + maxRightBorderSum); 

    }

    long maxSubSum3(const vector<int>& a) 

    {

    return maxSumRec(a, 0, a.size()-1); 

    }

    另外max3(long,long,long)表示求三个long中的最大值:

    //求出三个long中的最大值

    long max3(long a, long b, long c) 

    {

    if (a < b) 

    a = b; 

    if (a > c) 

    return a; 

    else 

    return c; 

    }

    对这个算法进行分析:

    T(1) = 1

    T(N) = 2T(N/2) + O(N)

    最后得出算法的复杂度为:O(NlogN) 。

    补充部分 来自 <http://www.cnblogs.com/CCBB/archive/2009/04/25/1443455.html>

  • 相关阅读:
    Mybatis3详解(一)----Mybatis的介绍
    【k8s】svc-sessionAffinityConfig
    【k8s】svc-sessionAffinity
    【k8s】svc-selector
    【k8s】svc-publishNotReadyAddresses
    【k8s】svc-ports
    【k8s】svc-externalIPs
    【k8s】svc-clusterIPs
    【k8s】svc-clusterIP
    【k8s】Service
  • 原文地址:https://www.cnblogs.com/Chuckqgz/p/5635487.html
Copyright © 2011-2022 走看看