编程之美----数组的最长递增子序列

DESCRIPTION:

数组的最长递增子序列(LIS)

Given a sequence of n real numbers a1, ..., an, determine a subsequence (not

necessarily contiguous) of maximum length in which thevalues in the sub-

sequence form a strictly increasing sequence.

 这是一个动态规划的问题,个人觉得DP最关键的是首先写出问题的子问题空间,

遵循的原则如下,(参考自算法导论)

      子问题空间越简单越好,必要的时候才进行扩充

本人打算专门写一篇blog关于自己选择子问题空间的一些经验。

 

Definition: (定义非常关键,数学证明中,首先必须非常清楚定义,而且有的时候好的简单的对子问题空间的定义,有利于构造高效的算法)

array[1-length]    输入数组

LIS[1-length]     前i个元素中以array[i]为最大元素的最长递增子序列(LIS)的长度

                (还可以有第二种定义:前i个元素中的最长递增子序列的长度, 注意,这个定义,并不一定要求以array[i]为最大元素)

算法1:

  LIS若采用第一种定义:   前i个元素中以array[i]为最大元素的最长递增子序列(LIS)的长度

算法非常直接,递推关系式如下

   LIS[i+1]= max{LIS[i-k] + 1}, array[k] < array[i+1], for any k <=i

意思是:

  以array[i+1]为最大元素的LIS,的倒数第二个元素array[k],可以选择那些array[i+1]前面的,并且值小于array[i+1]的元素。

很容易证明,满足最优子结构。

注意:最优值并不一定在 LIS[N]取得,因为现在 LIS[i]表示的是前i个元素中以array[i]为最大元素的LIS,所以 OPT = MAX(L[i]])

复杂度:  N个子问题,每个子问题计算耗时O(N), 最后找最大值O(N)

       So, complexity = O(N^2) + O(N) = O(N^2)

 

算法2:(错误的算法

  LIS若采用第二种定义:前i个元素中的最长递增子序列的长度。

这种定义有一个好处,最优值就是LIS[N]。因为,LIS[i+1]至少应该比LIS[i]一样好。。

所以LIS[]这个数组一定是非递减的。。

在算法1递推关系式的基础上,加上LIS[i+1与L[i]取max即可]

但是,

LIS[i+1] =max{LIS[i],  max{LIS[i-k] + 1}, array[k] < array[i+1],for any k <=i}

 实现非常简单,代码实现多了这一句,If(LIS[i] > LIS[i+1]) LIS[i+1] =LIS[i];

在这个子问题空间的定义下,上述的递推关系式是错误的!!!!

首先,我给出一个反例:  

   E.g.  array:{5, 6, 2, 3}

最终的LIS[]将是  1 2 2 3, OPT =3,

而该问题的正确结果是2, {5,6}或者{2,3}

分析错误的原因:

   LIS数组在这种新的定义下,array[k] < array[i+1]并不能保证倒数第二个元素比array[i+1]小,因为此时的子问题LIS[k]并不一定以array[k]为最大元素,例如反例中,LIS[3]=2, 子序列是{5,6}并不以第三个元素也就是3为最大元素,所以出错。


算法3:

引入新的数组:

maxV[length+1]

  长度为1的递增子序列最大元素的最小值为maxV[1]

  长度为2的递增子序列最大元素的最小值为maxV[2]

  …

  长度为LIS[i]的递增子序列最大元素的最小值为maxV[LIS[i]]


首先,证明maxV[]是递增的,因此可以使用二分搜索

用数学归纳法即可证明,下面证明

若前k个元素是递增的,一定有maxV[k+1] >= maxV[k]

PROOF:假设不成立,则maxV[k+1] < maxV[k]

根据maxV[k+1]的定义,存在一个长度是k+1的LIS,并且以maxV[k+1]为最大元素。

将上述子序列去掉最后一个元素maxV[k+1], 得到

  长度为k,且最大元素 < maxV[k+1] < maxV[k]

这显然与maxV[k]的定义矛盾。。所以假设不成立

 

编程之美》上给出了算法的代码。。

算法的思路基本是:

  每一步,将array[i]在当前的 maxV[]数组中进行二分搜索,找到位置k

maxV[k] < array[i] < maxV[k+1]

注意,array[i]加入后,仅仅影响maxV[k+1]  (maxV[k+1] = array[i])

而对其他的元素不产生影响。

证明:

1.      maxV[k]已经它以前的元素不变..较为简单。

2.      maxV[k+2]之后的元素不变。。

假设maxV[k+2]需要改变,即加入array[i]后,存在一个长度为k+2的LIS,以array[i]为最大元素。

将上述LIS去掉array[i]得到一个长度为k+1, 且最大元素 < array[i] < maxV[k+1]

的LIS,

这显然与maxV[k+1]的定义矛盾。。得证

 复杂度 = O(nlgn)

代码如下:

int LIS_new(int *a, int n)
{
    if(n <= 0) return 0;
    vector<int> maxV;
    maxV.push_back(a[0]);
    for(int i=0; i<n; ++i)
    {
        if(a[i] > *maxV.rbegin())
            maxV.push_back(a[i]);
        else
            *lower_bound(maxV.begin(), maxV.end(), a[i]) = a[i];
    }
    return maxV.size();
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值