原题:896. 最长上升子序列 II - AcWing题库

参考做过的 上升子序列

主要的不同是数据范围变大,原本的复杂度被卡了

考虑优化:

  • 首先分析样例,发现如果从头到尾遍历,一些数,例如3 1不需要考虑3,因为能比3大的都比1大,且1更优
  • 那这样的话,对于某种长度的子序列,只要记录下结尾最小的那组就可以了(可以是更长子序列的子集)
  • 发现不同长度的子序列,每种长度序列的结尾的最小值,呈现随序列长度变长,值变大的趋势。
  • 每次遍历到一个数,如果这个数要加到序列里,且希望构成最长的子序列,那么这个数就要加到结尾恰好小于它的最长的子序列之中。

得出算法:

  • 维护一个数组,这个数组存下每种长度子序列中,结尾最小的那一组的结尾值
  • 二分求这个数组里恰好小于查询数的序列
  • 这个数加到这个序列里,因为长度再长一点的序列结尾值比这个大(前面二分的结果),所以这个数加进去后,原有的长度加1,且结尾值小于原本这个长度的序列,更新数组
#include <iostream>
#include <algorithm>
 
using namespace std;
 
const int N = 100010;
 
int a[N], q[N];
 
int main()
{
    int n;
    cin >> n;
    
    for(int i = 0; i < n; i ++)  cin >> a[i];
    
    int len = 0;
    
    for(int i = 0; i < n; i ++)
    {
        int l = 0, r = len;
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(q[mid] < a[i]) l = mid;
            else r = mid - 1;
        }
        //如果找到的位置加1后大于最大长度len,len就变为r+1
        len = max(len, r + 1);
        
        // 找到位置加进去后,构成的子序列比原来多一位,而且加进去的值小于原来在r+1的值
        q[r + 1] = a[i];
    }
    
    cout << len << endl;
    
    return 0;
}