Skip to content

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