最长上升子序列
算法描述:
给定一个序列a,求出最长的严格递增的子序列的长度。
子序列:在保证a中元素顺序不变的情况下,删除若干个元素得到的序列为子序列
一个很显然的思路:
我们考虑序列
- 当i=1时,该序列LIS长度显然为1,所以
=1。 - 当i=2时,若
> 则,LIS长为2,即 =2,否则,LIS仍为1,即 =1。 - 当i=3时,在
到 之间寻找小于 的数,对于比 小的数,便可以考虑把 加入到序列中,从而LIS长度加一,即 =1+ 。
…以此类推 - 当i=k时,在
到 之间找出所有 < ,并取出max{ },则 =max{ }+1。
得到所有的dp后,dp中的最大值就是LIS的最大长度。
由此我们得到了LIS的递推求法:
1 | for(int i=1;i<=n;i++) |
分析其复杂度,显然该算法时间复杂度为O(
对于O(
分析上述递推算法,
显然
在实际例子中,我们能够发现dp数组是单调增的,为什么呢?以下用反证法简单证明:
如果dp不是单调增,即存在i < j,但是
那么知道这个性质之后,问题就好办多了。
我们遍历数组a:
举例a=3,2,4,5,3,7
- 当i=1时,
=3。{dp=3} - 当i=2时,由于
=2小于 =3,那么 =2显然是更优的。{dp=2} - 当i=3时,我们可以直接将
加入进dp后面,因为4>2。{dp=2,4} - 当i=4时,与前面同理。{dp=2,4,5}
- 当i=5时,我们可以替换掉
,因为3<4,存在子序列{2,3}优于子序列{2,4}。{dp=2,3,5}
…
由此可见,我们对序列的操作实质上就是在dp中寻找比
由于dp升序,这个操作可以用二分查找实现。
而对于未找到的情况,则说明dp中所有数据都小于
最终的答案显然就是dp的长度。
于是我们就有了贪心+二分的算法:
分析其复杂度,显然时间复杂度为O(nlogn),空间复杂度为O(n)。
Code
1 | vector<int>dp; |
总结
后者算法显著优于前者(笑~)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 N0Ne-Zer0's Blog!




