一.朴素算法
关于最长上升或不下降的子序列的朴素求法我就不用说了吧,直接一个$$O(n^2)$$的时间复杂度,附代码:
#include <cstdio>
#define M 100005
#define r register //加速器而已
#define max(a, b) a > b ? a : b
int n, a[M], LIS[M], ans; //a是原数组
int main (){
scanf("%d", &n);
for(r int i = 1; i <= n; i ++){
scanf("%d", &a[i]);
LIS[i] = 1;
}
for(r int i = 1; i <= n; i ++){
for(r int j = 1; j < i; j ++){
if(a[j] < a[i])
LIS[i] = max (LIS[i], LIS[j] + 1);
}
}
for(r int i = 1; i <= n; i ++)
ans = max (ans, LIS[i]);
printf("%d\n", ans);
return 0;
}
但这样的时间复杂度在处理大数据的时候太浪费时间了,于是我们引进STL。
二.STL
在这里,我们先引进两个函数:
1.lower_bound与upper_bound
我在这里简单释义一下:lower_bound(起始位置,结束位置,查找值):返回数组中第一个大于或等于查找值的元素的位置
upper_bound(起始位置,结束位置,查找值):返回数组中第一个大于查找值的元素的位置
2.实践思路
思路很简单,就是:每个数组元素放进LIS数组时都用 lower_bound或upper_bound找到LIS数组中第一个大于或等于这个元素的数的位置,把那个数给替换掉。就比如:
放进前LIS:1 2 3 7 8
待放进元素: 5
放进后LIS:1 2 3 5 8
那么,为什么这样做呢?道理也很简单,我们把第一个大于或等于待放进元素的数替换掉,就给后面进入LIS数组的元素提供了更多的空间,能尽量保证到LIS数组中元素最多。就比如:
放进前LIS:1 2 3 7 8
待放进元素:5 6 8
放进后LIS:1 2 3 5 6 8
懂了吧?
3.代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define M 200005
int a[M], lis[M], n, len;
inline void Read (int &x){
int f = 1; x = 0; char c = getchar();
while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {x = x * 10 + c - 48; c = getchar();}
x *= f;
}
int main (){
Read (n);
for (register int i = 1; i <= n; i ++)
Read (a[i]);
for (register int i = 1; i <= n; i ++){
if (a[i] > lis[len])
lis[++ len] = a[i];
else{
int idx = lower_bound (lis + 1, lis + len + 1, a[i]) - lis;
lis[idx] = a[i];
}
}
printf ("%d\n", len);
return 0;
}
三.运用题:变异最长上升序列
1.题目
题目描述
给出一个长度为N的整数序列,求出包含它的第K个元素的最长上升子序列。
输入
第一行两个整数N, K
第二行N个整数
输出
如题目所说的序列长度。
样例输入
8 6
65 158 170 299 300 155 207 389
样例输出
4
2.思路
怎么样,这道题目很简单吧,可以用两个最长上升子序列实践。
对于k以前的元素,去掉比k大或等于的元素;对于k后面的元素,去掉比k小或等于的元素。然后在k前面和后面分别找一个最长上升序列,把两个最长上升序列的元素加起来再加上k就行了。
3.代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define M 200005
int a[M], lis[M], n, k, len, ans;
bool flag[M];
inline void Read (int &x){
int f = 1; x = 0; char c = getchar();
while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {x = x * 10 + c - 48; c = getchar();}
x *= f;
}
int main (){
Read (n);
Read (k);
for (register int i = 1; i <= n; i ++)
Read (a[i]);
for (register int i = 1; i < k; i ++)
if (a[i] < a[k])
flag[i] = 1;
for (register int i = k + 1; i <= n; i ++)
if (a[i] > a[k])
flag[i] = 1;
for (register int i = k + 1; i <= n; i ++){
if (!flag[i])
continue;
if (a[i] > lis[len])
lis[++ len] = a[i];
else{
int idx = lower_bound (lis + 1, lis + len + 1, a[i]) - lis;
lis[idx] = a[i];
}
}
ans += len;
memset (lis, 0, sizeof(lis));
len = 0;
for (register int i = 1; i <= k - 1; i ++){
if (!flag[i])
continue;
if (a[i] > lis[len])
lis[++ len] = a[i];
else{
int idx = lower_bound (lis + 1, lis + len + 1, a[i]) - lis;
lis[idx] = a[i];
}
}
ans += 1 + len;
printf ("%d\n", ans);
return 0;
}
四.结语
以后的最长上升子序列问题就用STL吧!