📅  最后修改于: 2023-12-03 15:40:17.450000             🧑  作者: Mango
本文介绍了一种求解最长递增子序列(Longest Increasing Subsequence,LIS)的高效算法。该算法的时间复杂度为O(nlogn),比暴力枚举和动态规划算法效率更高。
给定一个序列,从中选出若干个数,使得它们按照原序列中的顺序构成一个递增的序列,并且这个递增序列的长度尽可能长。例如,对于序列 {6, 2, 8, 5, 1, 7},其中一个最长递增子序列是 {2, 5, 7},长度为3。
该算法的核心思想是维护一个长度逐渐增大的子序列,并在其中不断更新,使得该子序列的末尾元素尽可能的小。具体步骤如下:
建立一个空的数组b,用来存储当前最长递增子序列。令b[0]为原序列中的第一个数,令len为1表示这个最长递增子序列的长度。
从原序列中的第二个数开始遍历,对于每个数a[i],找到b中最后一个小于等于a[i]的数,令其下标为j。
如果b中不存在小于等于a[i]的数,则将a[i]添加到b的末尾,更新len。
否则,将b[j+1]替换为a[i],更新len。
很显然,每次替换后得到的b仍然是一个长度递增的递增子序列,且其中末尾元素尽量小。最终得到的b即为最长递增子序列。
下面是C++代码实现,其中LIS()函数返回最长递增子序列的长度。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int a[N], b[N], len;
int LIS()
{
b[0] = a[0];
len = 1;
for (int i = 1; i < n; i++) {
if (a[i] > b[len - 1]) {
b[len++] = a[i];
} else {
int j = lower_bound(b, b + len, a[i]) - b;
b[j] = a[i];
}
}
return len;
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
cout << LIS() << endl;
return 0;
}
LIS()函数中循环体内部只进行了一次二分查找操作,二分查找的时间复杂度为O(logn),因此总时间复杂度为O(nlogn)。
本文介绍了最长递增子序列问题,并给出了一种时间复杂度为O(nlogn)的高效算法。对于需要求解最长递增子序列的问题,在数据范围合理的情况下,可以优先考虑该算法。