895. 最长上升子序列 (LCIS)¶
给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式¶
第一行包含整数N。
第二行包含N个整数,表示完整序列。
输出格式¶
输出一个整数,表示最大长度。
数据范围¶
1≤N≤1000, −109≤数列中的数≤109
输入样例:¶
7
3 1 2 1 8 5 6
输出样例:¶
4
DFS¶
可记录方案,但是不够高效 (空间)
- a: 数组
- k[i]: 以a[k[i]]为结尾的最长子序列的长度
- 答案即:k中的最大值
int n, a[105];
int k[105], ans;
void dfs(int len) {
if (len > ans) ans = len;
// 找从当前最长的子序列的最后到a的结尾中--第一个比当前结尾要大的数字的index
for (int i = k[len] + 1; i <= n; ++i) {
if (a[i] > a[k[len]]) {
k[len + 1] = i;
dfs(len + 1);
}
}
}
// initial condition
k[0] = 0; // 长度为0的时候,最长子序列的长度是0
a[0] = -(1 << 30); // -inf作为数组的首个元素,即a[1]必定会成为一个长度为1的递增子序列
dfs(0); // 从长度为0的数组开始遍历
-
Time: O(n^2): 每次遍历都需要走一个forloop,最坏情况是本身就是一个递增的子序列
-
Space: O(n): n层遍历和开辟的两个新的数组
DFS Enhanced¶
不使用k数组,因为每次只需使用k[end]来比较k[end] ~ a[end]的部分中比a[k[end]]大的第一个数字即可
int n, ans, a[105];
void dfs(int len, int tail) {
if (len > ans) ans = len;
// 找从当前最长的子序列的最后到a的结尾中--第一个比当前结尾要大的数字的index
for (int i = tail + 1; i <= n; ++i) {
if (a[i] > a[tail]) {
dfs(tail + 1, i);
}
}
}
// initial condition
dfs(0, 0); // 从长度为0的数组开始遍历,同时初始长度为0
-
Time: O(n^2): 每次遍历都需要走一个forloop,最坏情况是本身就是一个递增的子序列
-
Space: O(n): n层遍历和开辟的一个新的数组
DP¶
找子结构:假设结尾为a[i]的最长子数组的长度是f[i],那么结尾为a[i+1]的最长子数组的,最后答案是f[end]
方法一: 从前往后,每次更新新的元素的长度
f[0] = 0;
for (int i = 0; i < n; ++i)
for (int j = i + 1; j <= n; ++j) // 从当前的下一个元素往后找,更新新的位置的最大子数组长度
if (a[j] > a[i])
f[j] = max(f[j], f[i] + 1);
方法二: 从后往前,每次更新当前元素的长度
f[0] = 0;
for (int i = 1; i <= n; ++i)
for (int j = 0; j < i; ++j) // 从当前的上一个元素往前找,更新当前位置的最大子数组长度
if (a[i] > a[j])
f[i] = max(f[i], f[j] + 1);
Last update:
February 2, 2021