问题描述
一个数的序列ai,当a1<a2<…<as的时候,我们称这个序列是上升的。对于给定的一个序列(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
算法思想
1.求子问题
求序列的前n个元素的最长上升子序列的长度是个子问题,但是这样分解子问题,不具有无后效性
假设F(n)=x,但可能有多个序列满足F(n)=x。有的序列最后一个元素比an+1小,则加上n+1就能形成更长的上升子序列,有的序列最后一个元素不比an+1小…以后的事情受如何达到状态n的思想,不符合无后效性
求以ak(k=1,2,3,…N)为终点的最长上升子序列的长度
一个上升子序列中最右边的那个数,称为该子序列的重点
虽然这个子问题和原问题形式上并不完全一样,但是只要这个N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。
2.确定状态
子问题只和一个变量—数字的位置相关,因此序列中位置k就是状态,而状态k对应的值,就是以ak作为终点的最长上升子序列的长度
状态一共有n个
3.找出状态转移方程
maxLen(k)表示以ak作为终点的最长上升子序列的长度,那么
初始状态maxLen(1)=1
maxLen(k)=max{maxLen(i):1<=i<k且ai<ak且k≠1}+1,若找不到这样的i,则maxLen(k)=1
maxLen(k)的值,就是在ak左边,终点数值小于ak,且长度最大的那个上升子序列的长度再加1.因为ak左边任何终点小于ak的子序列,加上ak之后就能形成一个更长的上升子序列。
程序代码
#include<iostream>
#define MAXSIZE 1000
using namespace std;
//时间复杂度为O(n2)
int ascendList(int a[],int n){
//maxLen[k]表示以ak为终点的最长上升子序列的长度
int maxLen[MAXSIZE];
//这里为了方便,就所有元素的最长上升子序列初始化为1
for(int i=0;i<n;i++){
maxLen[i]=1;
}
//从每个元素开始遍历,计算其最长上升子序列的长度
for(int i=1;i<n;i++){
for(int j=i;j>=0;j--){
//maxLen[k]的值等于在ak左边,终点值小于ak,且长度最大的那个上升子序列的长度再加1
if(a[i]>a[j]&&maxLen[i]<=maxLen[j]){
maxLen[i]=maxLen[j]+1;
}
}
}
//找出整个数组中最长的那个上升子序列
int maxL=maxLen[0];
for(int i=0;i<n;i++){
if(maxLen[i]>maxL){
maxL=maxLen[i];
}
}
return maxL;
}
int main(){
int a[MAXSIZE];
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
cout<<ascendList(a,n);
return 0;
}