最长不下降子序列O(N*logN)

此篇文章为洛谷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;
}

猜你喜欢

转载自www.cnblogs.com/liuzongxin/p/10304354.html
今日推荐