AcWing 896. 最长上升子序列 II
题目链接:896. 最长上升子序列 II
题目描述
给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式
第一行包含整数 N。
第二行包含 N 个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围
1 ≤ N ≤ 100000,
−109 ≤ 数列中的数 ≤ 109
输入样例
7
3 1 2 1 8 5 6
输出样例
4
题目分析:
观察题目,可以看到,数据范围 N 比较大,如果用线性 DP,时间复杂度为 N2,题目会超时,因此考虑进一步优化
算法1:二分 + 贪心(时间复杂度为 nlogn)
import java.util.*;
public class Main{
static final int N = 100010;
static int[] a = new int[N];
//q 数组存放每个子序列的末尾值
static int[] q = new int[N];
static int n;
public static void main(String[] args){
Scanner in = new Scanner(System.in);
n = in.nextInt();
for (int i = 1; i <= n; i++){
a[i] = in.nextInt();
}
int len = 0;
for (int i = 1; i <= n; i++)
{
int l = 0, r = len, mid;
while (l < r)
{
//除以2的操作变成位运算,代码运行速度更快
mid = l + r + 1 >> 1;
//寻找 q[k] 小于 a[i],且 q[k+1] > a[i]的下标 k
if (q[mid] < a[i]) l = mid;
else r = mid - 1;
}
//递增序列增加一个元素
//分两种情况:
//(1)数组最后一个元素比a[i]小的话,则将元素放到最后一个位置
//(2)数组中间的某个元素 q[k] 比 a[i] 小的话(a[k+1] > a[i]),则将元素放到 k+1 的位置上,即a[k+1] = a[i]
if (r == len) len++;
//q 数组是单调递增的,最后的结果存放的是最长递增子序列的每个元素
q[r + 1] = a[i];
}
System.out.println(len);
}
}
算法2:单调栈
import java.util.*;
public class Main{
static final int N = 100010;
static int[] a = new int[N];
static int top = 0;
static int[] stack = new int[N];
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
for (int i = 1; i <= n; i++){
a[i] = in.nextInt();
}
stack[top++] = a[1];
for (int i = 2; i <= n; ++i) {
//a[i]大于栈顶元素,直接将该元素入栈
if (a[i] > stack[top - 1]){
stack[top++] = a[i];
}else{
int index = top - 1;
//从后往前扫描栈,将第一个大于或者等于 a[i] 的数替换成 a[i]
while(index >= 0 && stack[index] >= a[i]){
index--;
}
stack[index + 1] = a[i];
}
}
System.out.println(top);
}
}