题目描述
给定一个长度为 n 的数列 a0, a1, a2...an-1
,求出这个序列中的最长的上升子序列的长度,上升子序列的定义为:对于任意的 i<j
,都满足 ai<aj
。
**限制条件:**1≤n≤1000,0≤a≤1000000
样例:
输入:
n = 5
a = {4, 2, 3, 1, 5}
输出:
3(a1, a2, a4构成的子序列2,3,5最长)
题解
这个问题就是著名的最长上升子序列(LIS,Longest Increasing Subsequence)问题,这个问题有两种解法,第一种解法是 O (n²) 的 DP 解法,第二种解法是 O (nlogn) 的 DP 加二分解法。
O (n²) 算法
首先我们可以来建立一下 DP 的递推关系:
定义dp[i]:=以ai为末尾的最长上升子序列的长度
以 ai 结尾的上升子序列是:
只包含ai的子序列
在满足j<i并且aj<ai的以aj为结尾的上升子列末尾,追加上ai后得到的子序列
这二者之一。这样就能得到如下的递推关系:
dp[i]=max{1, dp[j]+1|j<i且aj<ai}
使用这个递推公式可以在 O (n²) 时间内解决这个问题。
代码如下:
// 输入
int n;
int a[MAX_N];
int dp[MAX_N];
void solve() {
int res = 0;
for (int i = 0; i < n; i++) {
dp[i] = 1;
for(int j = 0; j < i; j++) {
if (a[j] < a[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
res = max(res, dp[i]);
}
printf("%d\n", res);
}
这个方法比较简单,但是时间复杂度也比较高。下面我们来看看更优的解法。
O(nlogn)
之前我们的思路是求出以第 i 个元素为结尾的最长上升子序列长度,我们可以换个思路,考虑一下 dp[i]
为最长上升子序列长度为i情况下最小的元素
,这样我们就可以通过二分来进行优化,代码如下:
int dp[MAX_N];
void solve() {
fill(dp, dp+n, INF);
for (int i = 0; i < n; i++) {
*lower_bound(dp, dp + n, a[i]) = a[i];
}
printf("%d\n", lower_bound(dp, dp + n, INF) - dp);
}
相关文章
- LintCode 460. 在排序数组中找最接近的 K 个数
- LintCode 627. 最长回文串
- LintCode 428. x 的 n 次幂
- LintCode 415. 有效回文串
- [2017 年阿里基础架构实习生笔试在线编程题 题解](