最长上升子序列
问题描述
一个数的序列 ai ,当 a1< a 2< … < … <aS的时候,我们称这个序 列 是上升的。对于给定一个序列(a 1, a 2, …, , …, , …, 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 <= 1000) 。第二行给出序列中的 N个整数,这些数的取值范围都在 0到10000 。
输出要求:
最长上升子序列的度。
输入样例:
7
1 7 3 5 9 4 8
输出样例:
4
解题思路
1、找子问题
- “求序列的前n个元素的最长上升子序列的长度”,是个子问题,但是这样分别不具有无后效性(后面的事情只跟状态的值有关,跟这个状态是怎么到达这个值的没有关系)
(以后的事情不仅跟f(n)的值有关,还跟走到f(n)的走法里面最后一个元素的值跟a(n+1)比较的结果有关,不满足无后效性.)
- 以“求以ak为终点的最长上升子序列的长度”为子问题,虽然与原问题不太一样,但是将所有的子问题求解,找出其中最大的值,就是问题的答案,并且这个问题满足无后效性
2、确定状态
子问题只与一个变量有关,就是数字的位置k
3、转态转移方程
初始转态:maxlen(1)=1
转移方程:maxlen(k)=max{maxlen(i):1≤i<k,且ai<ak,k!=1}+1
maxlen(k)就是在ak左边的点中,“终点”的数值<ak,并且上升子序列长度最大的,序列长度+1
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX = 1010;
int a[MAX];//存放所有的数
int maxlen[MAX];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
maxlen[i]=1; //如果最终在这个数前面找不到比它小的
//那么以它为终点的最长子序列就是1
}
//maxlen[1]这是边界条件也就是1了
//接下来以边界条件为基础,向后推算
for(int i=2;i<=n;i++){
//每次求以第i个数为终点的最长上升子序列的长度
for(int j=1;j<i;j++){
//查看a[i]左边的所有的数j,看以第j个数为终点的最长上升子序列的长度
if(a[i]>a[j]){
//可以在以a[j]为终点的最长上升子序列个数+1,得到更长的上升子序列,且以a[i]为终点
maxlen[i]=max(maxlen[i],maxlen[j]+1);//之前更新的maxlen[i]可能会比此次更新大,所以要比较一下
}
//如果没有这样的a[j],则还是初始值1
}
}
cout<<* max_element(maxlen+1,maxlen+1+n);
//用max_element算法求数组中的组大值
return 0;//时间复杂度O(n^2)
}
新的知识点
- 用* max_element()求数组中的最大元素
- 为数组赋初始值作为边界条件