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] ,那么这个长度就是 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;
}


猜你喜欢

转载自blog.csdn.net/qq_44791484/article/details/114823416