题目

给定一个长度为 N的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。
第二行包含 N个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤100000,
−10e9≤数列中的数≤10e9

当前题目为Acwing 896.最长上升子序列Ⅱ 在Ⅰ的基础上对N 的范围进行了扩大,背包的思考方式存在时间复杂度压力,所以采用模拟单调栈的贪心思路求解。

代码一(采用low_bound和容器)

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    int n; cin >> n;
    vector<int>arr(n);
    for (int i = 0; i < n; ++i)cin >> arr[i];

    vector<int>stk;//模拟堆栈
    stk.push_back(arr[0]);
    
    for (int i = 1; i < n; ++i) {
        if (arr[i] > stk.back())//如果该元素大于栈顶元素,将该元素入栈
            stk.push_back(arr[i]);
        else//替换掉第一个大于或者等于这个数字的那个数
            *lower_bound(stk.begin(), stk.end(), arr[i]) = arr[i];
    }
    cout << stk.size() << endl;
    return 0;
}

代码二(使用二分查找模拟单调栈的替换位置)

代码二的时间复杂度是代码一的四分之一

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100010;
//a[]存放数字 f[]模拟单调栈
int a[N],f[N];
//最长子序列的长度
int cnt;

int find(int x)
{
    int l = 1,r = cnt;
    while(l < r)
    {
        int mid = l + r>> 1;
        if(f[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return l;
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
    f[++cnt] = a[1];
    for(int i = 2;i <= n;i++)
    {
        if(a[i] > f[cnt]) f[++cnt] = a[i];
        else 
        {
            //find函数找的是在0-cnt个数字中比a[i]小或者等于的那个数字的位置
            int tmp = find(a[i]);
            f[tmp] = a[i];
        }
    }
    printf("%d",cnt);
    return 0;
}