acwing 896 最长上升子序列II (二分单调优化)

题面

在这里插入图片描述

题解

  1. 对于未优化的最长上升子序列问题,我们用 f[i] 来表示以 i 结尾的最长上升子序列 ,然后枚举 i 前面的数,来更新 f[i] 。 这样是 O(n2) 的,此题数据范围大,会超时

在这里插入图片描述
优化版其实是贪心的思想,对于长度相同的上升子序列, 1 3 5 和 1 2 7 其实我们只需要保留结尾数字小的即可 ,因为结尾越小,我们下一次更新就有可能更多,比如下一个数字是6,那么1 3 5 就可以更新成 1 3 5 6 ,但是 1 2 7 却不可以,所以我们就可以用一个 f 数组 来记录长度为 1 2 3 . . . 的上升子序列结尾的数字 ,例如 f[1]=1 。我们还可以发现,f 数组也是单调递增的(可以自己证明)

  1. 那么每次从前往后枚举 a[i] , 就可以用二分来找到一个f[mid] , 使得a[i] 是大于 f[mid] 的最小值,也就是 f[mid+1]> a[i] > f[mid] ,那么我们就可以在f[mid]这个长度的队列中加上这个a[i],长度就变成了r + 1 ,又因为这个序列肯定是小于原来r+1长度的序列,所以更新 f[r+1] 位置上的值使其尽可能的小
  1. 最终的 len 就是 最长上升子序列的长度 ,枚举 O(n) ,二分 O(logn) ,总的时间复杂度 O(nlogn)

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e5 + 10;

int n;
int a[N];
int f[N];


int main() {

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    int len = 0;
    for (int i = 1; i <= n; i++) {
        int l = 0, r = len;
        while (l < r) {
            int mid = (l + r + 1) >> 1;
            if (f[mid] < a[i]) l = mid;
            else r = mid - 1;
        }
        len = max(len, r + 1);
        f[r + 1] = a[i];
    }
    cout<<len<<endl;


    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值