最长单调上升子序列(LIS) O(nlogn)求法

常规的dp求LIS的时间复杂度为O(n2),对于n比较大的时候这是不能接受的。这时候我们就需要一个优秀的O(nlogn)的算法了。

这个算法是基于贪心的思想,具体来说就是开一个序列数组b,记录已经求得的“最长上升子序列”,当扫到一个元素大于序列b的最后一个元素时,就直接将扫到的元素加入序列b,否则就在b数组中二分查找第一个大于扫到的元素的元素,将其替换,因为这样序列的“潜力值”更大。

假设我们有一个序列{1,8,3,7,11,6,9,10}。
初始化b数组的第0个元素小于序列中的最小元素(比如0或-1,上次就被这个坑了)。
  扫描到值1,1大于b中第0个元素,此时b为{1}
  扫描到这8,8大于b末尾元素,此时b为{1,8}
  扫描到3,3小于b末尾元素,二分查找替换掉8,此时b为{1,3}
  扫描到7,直接加入,此时b为{1,3,7}
  扫描到11,直接加入,此时b为{1,3,7,11}
  扫描到6,小于末尾元素,二分查找替换掉7,此时b为{1,3,6,11}(此时b并不是按照a中的顺序来了)
  扫描到9,小于末尾元素,二分查找替换掉11,此时b为{1,3,6,9}
  扫描到10,直接加入,此时b为{1,3,6,9,10}

看完举的栗子,相信大家都多多少少明白,b中不一定记录的是a的单调上升序列,但是b数组的长度就是最长单调上升序列的长度了。

这里以POJ Longest Ordered Subsequence给出代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 using namespace std;
 5 
 6 int a[1001],b[1001];
 7 
 8 int n,len;
 9 int main(){
10     b[0]=-1;
11     scanf("%d",&n);
12     for(int i=1;i<=n;++i) scanf("%d",&a[i]);
13     for(int i=1;i<=n;++i){
14         if(a[i]>b[len]){
15             b[++len]=a[i];
16         }else{
17             int k=lower_bound(b+1,b+1+len,a[i])-b;
18             b[k]=a[i];
19         }
20     }
21     cout<<len;
22     return 0;
23 }

 

转载于:https://www.cnblogs.com/Asika3912333/p/11381769.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值