10.3例题:最长上升子序列

来源:互联网 发布:mac怎么连iphone 编辑:程序博客网 时间:2024/06/07 06:26

问题定义:

一个数的序列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。


输出要求


最长上升子序列的长度。


输入样例 





1 7 3 5 9 4 8


输出样例 





解题思路


如何把这个问题分解成子问题呢?经过分析,发现“求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度”是个好的子问题――这里把一个上升子序列中最右边的那个数,称为该子序列的“终点”。虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。


由上所述的子问题只和一个变量相关,就是数字的位置。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以ak做为“终点”的最长上升子序列的长度。这个问题的状态一共有N个。状态定义出来后,转移方程就不难想了。假定MaxLen (k)表示以ak做为“终点”的最长上升子序列的长度,那么: 


MaxLen (1) = 1 
MaxLen (k) = Max { MaxLen (i):1<i < k且 ai < ak且 k≠1 } + 1
这个状态转移方程的意思就是,MaxLen(k)的值,就是在ak左边,“终点”数值小于ak,且长度最大的那个上升子序列的长度再加1。因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列。


实际实现的时候,可以不必编写递归函数,因为从 MaxLen(1)就能推算出MaxLen(2),有了
MaxLen(1)和 MaxLen(2)就能推算出MaxLen(3)……

代码示例:

/***Declaration:The author of <<Accelerated C++>> has wrote in the end of that book: As you look for reading materimal, keep in mind that books on the shelf do not make you a better programmer. Ultimately, the only way to improve your programming is to write programs.>这些程序来自一些ACM书籍,作者只为提高编程能力,实现书中例题或练习。如有侵权,请联系作者,作者将立即删除。**联系邮箱:mingxinglai#gmail.com**/#include <stdio.h>#include <memory.h>#define MAX_N 1000int b[MAX_N + 10];int aMaxLen[MAX_N + 10];int main(int argc, char* argv[]){int N, i;scanf("%d", &N);for( i = 1; i <= N; i++)scanf("%d", &b[i]);aMaxLen[1] = 1;for( i = 2; i <= N; i++){int nTmp = 0;for( int j = 1; j < i; j++)if( b[i] > b[j] ){if( nTmp < aMaxLen[j])nTmp = aMaxLen[j];}aMaxLen[i] = nTmp + 1;}int nMax = -1;for( i = 1; i <= N; i++ )if( nMax < aMaxLen[i] )nMax = aMaxLen[i];printf("%d\n", nMax);return 0;}


原创粉丝点击