让你维护一个非升的序列和一个单调升序的序列
一个 O ( n l o g n ) O(nlogn) O(nlogn)的算法(最长单调上升子序列)
对一个有序的序列
l o w e r _ b o u n d ( b e g i n , e n d , k e y ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 大 于 等 于 k e y 的 数 字 的 位 置 lower\_bound(begin, end, key)-b[]:在begin到end-1中,查找到第一个大于等于key的数字的位置 lower_bound(begin,end,key)−b[]:在begin到end−1中,查找到第一个大于等于key的数字的位置
u p p e r _ b o u n d ( b e g i n , e n d , k e y ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 大 于 k e y 的 数 字 的 位 置 upper\_bound(begin, end, key)-b[]:在begin到end-1中,查找到第一个大于key的数字的位置 upper_bound(begin,end,key)−b[]:在begin到end−1中,查找到第一个大于key的数字的位置
l o w e r _ b o u n d ( b e g i n , e n d , k e y , g r e a t e r < i n t > ( ) ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 小 于 等 于 k e y 的 数 字 的 位 置 lower\_bound(begin, end, key, greater<int>())-b[]:在begin到end-1中,查找到第一个小于等于key的数字的位置 lower_bound(begin,end,key,greater<int>())−b[]:在begin到end−1中,查找到第一个小于等于key的数字的位置
u p p e r _ b o u n d ( b e g i n , e n d , k e y , g r e a t e r < i n t > ( ) ) − b [ ] : 在 b e g i n 到 e n d − 1 中 , 查 找 到 第 一 个 小 于 k e y 的 数 字 的 位 置 upper\_bound(begin, end, key, greater<int>())-b[]:在begin到end-1中,查找到第一个小于key的数字的位置 upper_bound(begin,end,key,greater<int>())−b[]:在begin到end−1中,查找到第一个小于key的数字的位置
l o w e r _ b o u n d 和 u p p e r _ b o u n d 只 有 一 个 等 于 的 差 别 lower\_bound和upper\_bound只有一个等于的差别 lower_bound和upper_bound只有一个等于的差别
对于一组数,当我们已经遍历到 i i i的时候,我们已经得到了一个最长的子序列 b [ l e n ] = x 1 , x 2 . . . x l e n b[len]={x_1,x_2...x_{len}} b[len]=x1,x2...xlen,如果 a [ i ] > b [ l e n ] a[i]> b[len] a[i]>b[len],那么乐意直接 b [ + + l e n ] = a [ i ] b[++len]=a[i] b[++len]=a[i],如果 a [ i ] ≤ b [ l e n ] a[i]\leq b[len] a[i]≤b[len],那我们可以通过修改 b b b中第一个大于等于 a [ i ] a[i] a[i]的数字来使得 b b b数组更有竞争力,
b [ l o w e r _ b o u n d ( b + 1 , b + 1 + l e n , a [ i ] ) − b ] = a [ i ] b[lower\_bound(b+1,b+1+len,a[i])-b]=a[i] b[lower_bound(b+1,b+1+len,a[i])−b]=a[i],这样我们持续更新这个 b b b数组,就可以得到一个最长上升子序列
为什么是 l o w e r _ b o u n d ? lower\_bound? lower_bound?
因为是单调上升,所以不存在有相同的元素,所以可以修改 b b b中的相同元素,
而如果是不单调的话,那么就不用修改相同的元素,就用 u p p e r _ b o u n d upper\_bound upper_bound
代码:
b[1] = h[0];
len = 1;
for (int i = 1; i < cnt; i ++) {
if(h[i] > b[len]) b[++len] = h[i];
else {
int pos = lower_bound(b+1, b+1+len, h[i]) - b;
b[pos] = h[i];
}
}
最长非升子序列:
与最长上升同理,我们只需要修改查找规则,因为是非升所以不用修改相同的元素,所以用 u p p e r _ b o u n d upper\_bound upper_bound
b[1] = h[0];
int len = 1;
for (int i = 1; i < cnt; i ++) {
if(h[i] <= b[len]) b[++len] = h[i];
else {
int pos = upper_bound(b+1, b+1+len, h[i], greater<int>()) - b;
b[pos] = h[i];
}
}
#include
using namespace std;
#define LL long long
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
const double eps = 1e-8;
typedef pair<int, int> psi;
void fre() {
if(freopen("洛谷P1020.txt", "r", stdin) == NULL)
system("touch 洛谷P1020.txt");
freopen("洛谷P1020.txt", "r", stdin);
}
int h[maxn];
int dp[maxn], b[maxn];
int main(int argc, char *args[]) {
fre();
int cnt = 0, x;
while(scanf("%d", &x) != EOF) {
h[cnt++] = x;
}
b[1] = h[0];
int len = 1;
for (int i = 1; i < cnt; i ++) {
if(h[i] <= b[len]) b[++len] = h[i];
else {
int pos = upper_bound(b+1, b+1+len, h[i], greater<int>()) - b;
b[pos] = h[i];
}
}
printf("%d\n", len);
b[1] = h[0];
len = 1;
for (int i = 1; i < cnt; i ++) {
if(h[i] > b[len]) b[++len] = h[i];
else {
int pos = lower_bound(b+1, b+1+len, h[i]) - b;
b[pos] = h[i];
}
}
printf("%d\n", len);
return 0;
}