一个数的序列bi,当b1 < b2 < ... < bS 的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... <iK <= N。
比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8). 你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入数据
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N 个整数,这些整数的取值范围都在0 到10000。
输出要求
最长上升子序列的长度。
输入样例
7
1 7 3 5 9 4 8
输出样例
4
我们首先来建立一下 递推关系。
定义dp[i]:=以a;为末尾的最长的上升子序列的长度
以a结尾的上升子序列是
只包含a;的子序列
在满足j<i并且a(j)<a(i),的以a,为结尾的上升子列末尾,追加上ay后得到的子序列这:者之一-。
这样就能得到如下的递推关系:dp[i]-max{1,dp[]+1j<i且a(j)<a(i)} 使用这一递推公式可以在0(n*n)时间内解决这个问题。
伪代码:
int n;
int a[MAX_N];
int dp[MAX_N];
void solve()
{
int res = O;
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) ;
}
此外还可以定义其他的递推关系。前而我们利用DP求取针对最末位的元素的最长的子序列。如果子序列的长度相同,那么最末位的元素较小的在之后会更加有优势,所以我们再反过来用DP针对相同长度情况下最小的末尾元索进行求解。
dp[i]=长度为i计1的 上升子序列中末尾元素的最小值(不存在的话就是INF )
我们来看看如何用DP来更新这个数组。
最开始全部dp[i]的值都初始化为INF。然后由前到后逐个考虑数列的元素,对于每个a,如果i=0或者dp[i-1]<a(j)的话,就用dp[i]=min(dp[i], a)进行更新。最终找出使得dp[i]<INF的最大的i+1就是结果了。这个DP直接实现的话,能够与前面的方法样在O(n2)的时间内给出结果, 但这一算法还可以进步优化。首先dp数列中除INF之外是单调递增的,所以可以知道对于每个a最多只需要1次更新。对于这次更新究竟应该在什么位置,不必逐个遍历,可以利用二分搜索,这样就可以在0(nlogn)时间内求出结果。