此篇文章为洛谷ID108949的用户的文章搬运。(系用户本人操作)
update on 2018/12/31:请自行查阅最长不上升子序列的函数(not_upper_bound)手写写法。(由于我不会写)
前言
此篇文章的完整性和正确性不能保证。不注明出处严禁转载。OI的学术环境,我们共同维护。
目录
1.序言
1.1 简述
2.警告
3.$N*log(N)$实现方法
- 3.1 思路
- 3.2 代码
-----
-----
## 序言
### 简述
最长不下降子序列(及其延展)是OI中的比较普遍的考点。其含义是,在一个数组中,找出最长的可以不连续的一个序列使得该序列是一个不下降序列。
-----
## 警告
此文章默认为学习过$n^2$算法的选手学习。
-----
## $N*logN$实现方法
### 思路
$N*logN$方法是:设f[i]表示长度为i、处在该数组中不下降子序列的最后一个元素。那么f[]一定是一个单调的序列。(既然不下降那一定单调)
具体做法是,
定义数组f[],a[](作用我们之前讲过)和len(记录最长不下降子序列的长度)。
然后从1到n循环,如果`f[len]<a[i]`,(当时直接可以接在后面,不冲突)那么说明可以接到`f[len]`后面,即:`f[++len]=a[i]`。
刚才我们讲到`f[len]<a[i]`的情况。如果`f[len]>=a[i]`呢?
这时候就用二分查找(推荐能用`upper_bound`和`lower_bound`就用)来找到第一个`f[j]>=a[i]`的位置并替换它(使得第`j`项尽可能的小,这样才可能接上更长的序列,不明白的可以手动模拟一下`a[]=1 5 2 3 4`这组数据)。
`upper_bound`使用方法
`upper_bound`返回的是单调不下降序列一个**左闭右开**的区间$[first,last)$里第一个大于`keyword`的元素的物理位置(即迭代器位置,通常为`0x3424f3f`不拉不拉的一大堆),所以计算成下标**应该减去数组首元素地址(即数组名)**。**如果没有找到,返回`last`。(此时下标越界!!!!)**
如输出在g数组第3个到第7个查找大于15的第一个元素下标,代码是
```cpp
cout<<upper_bound(d+3,d+7+1,25)-g;
```
`lower_bound`的使用方法和`upper_bound`基本相同。只是`lower_bound`查找的是序列中第一个大于等于`keyword`的位置罢了。(最长不下降用`lower_bound`)
### 代码(最长上升子序列)
# include <bits/stdc++.h> using namespace std; const int size=10003;//此处可以自定义 int len,f[size],n,a[size]; int main() { cin>>n; if(n==0) { cout<<0; return 0; } for(int i=1;i<=n;i++)cin>>a[i]; f[1]=a[1]; len=1; for(int i=2;i<=n;i++) { if(a[i]>f[len])f[++len]=a[i]; else { int j=upper_bound /*最长不下降改成lower_bound*/ (f+1,f+1+len,a[i])-f; f[j]=a[i]; } } cout<<len; return 0; }