最长上升子序列(笔记)

最长上升子序列

问题描述

一个数的序列 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()求数组中的最大元素
  • 为数组赋初始值作为边界条件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值