参考做过的 上升子序列
主要的不同是数据范围变大,原本的复杂度被卡了
考虑优化:
- 首先分析样例,发现如果从头到尾遍历,一些数,例如
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;
}