程序设计与算法----动态规划之最长公共子序列

问题描述

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值