最大子序列和问题

问题描述:给定一整数序列A1, A2,... An (可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大 

这是我前几天参加新浪广告算法组面试的时候一个题目。自己当时脑袋浆糊了,加上前面答的不好,这里答的时候脑子一片空白。哎~

这一题本质在于动态规划的运用。至于O(n^3), O(n^2),还有O(nlogn)的解法我就不多说了。

先上代码

int FindMaxSumSubSequence(int a[], int len)
{
     int max  = 0;
     int sum = 0;
     for(int i = 0; i < len; i++)
     {
           sum += a[i];
           if ( sum > max )
                max = sum;
           else if ( sum < 0)
               sum = 0;
      }
      return max;
}

关键在于如何理解这样一段代码。

网上大众的解释如下

“在这一遍扫描数组当中,从左到右记录当前子序列的和temp_sum,若这个和不断增加,那么最大子序列的和max也不断增加(不断更新max)。如果往前扫描中遇到负数,那么当前子序列的和将会减小。此时temp_sum 将会小于max,当然max也就不更新。如果temp_sum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时将temp_sum置为0。然后,temp_sum将从后面开始将这个子段进行分析,若有比当前max大的子段,继续更新max。这样一趟扫描结果也就出来了。” 

这是一种比较通俗化的描述,但没有深入动态规划的本质解释。尤其是如何解释“说明前面已经扫描的那一段就可以抛弃了”这样一句话。

下面将从动态规划的角度解释为什么可以在O(n)复杂度内解决问题

根据定义,我们可以定义bj

那么最大子段和为

由bj的定义知,b[j-1] > 0 时, b[j] = b[j-1] + a[j],否则b[j] = a[j]

故状态转移方程为b[j] = max{b[j-1]+a[j], a[j]}

故代码如下

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

写到这里 ,还需要解释几个比较让人迷惑的问题

1) b[j]到底是个什么东西?

b[j]本质上就是到下标为j的地方为止,连续子序列的最大和。

那么整个数组的最大子序列和就是max(b[i])

2) b[j]具有最优子结构么

当然,任意一个b[j]均由b[j-1]扩展而来,为什么,请结合问题1)。扩展的方式如上文所述。故可以用状态转移方程统一描述。

3) 为什么b[j-1] > 0  b[j] = b[j-1] + a[j],否则b[j] = a[j]

 看图,如果是负数,那么在此基础上扩展,和一定会变小,与其如此,不如选定新的和,即b[j] = a[j]

原文地址:https://www.cnblogs.com/ShaneZhang/p/3768773.html