• 最大子序列和问题


    问题描述:给定一整数序列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]

  • 相关阅读:
    linux将命令添加到PATH中
    查看linux服务器时间
    spring参数校验及全局异常处理
    Https协议的工作过程
    反爬虫方式
    telnet进入某个端口后无法退出
    索引操作
    redis output buffer 设置太小导致oom
    mysql查询课程浏览记录表中最近一周浏览次数最多的课程
    C++ 读写注册表
  • 原文地址:https://www.cnblogs.com/ShaneZhang/p/3768773.html
Copyright © 2020-2023  润新知