动态规划--(最长上升子序列 poj2533)

 最长上升子序列

 描述

 一个数的序列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个整数,这些整数的取值范围都在010000

 输出

 最长上升子序列的长度。

 样例输入

 7

 1 7 3 5 9 4 8

 样例输出 

 4

 


分析:


1.最优子结构问题:

 在一个数据串中,如: 1 7 3 5 9 4 8,如果我要求解在9的时候,求出最长的上升子序列,其实,平时最简单的方法就是,

 我能不能在已经前面计算过的的所有的最长序列中,找到一个最长的,再把自己这个元素加上去;

2.子问题重叠:

每一次都是在前面的每一个一个元素中,找到最长的个数了,所以,重叠问题很多,每到一个元素,就必须去求解前面的元素的最长个数

 考虑到这点,如果是用一个数组将每一个元素的所求的解都储存,那就不是将时间复杂度减下来了吗?;

3.边界:

 肯定就是第一个元素了,第1个元素的最长上升子序列就是1啦,所以就有dp[0] = 1;注意,下标从0 开始

4.子问题独立:

每一次的求解都是一个子问题,和后面的数字没有关系;

5:选择个数:可以是判断的次数:这个题目的选择个数是和他的访问的个数有关,所以是不确定的,但是有规律,就是访问到第几个数据,就有几次判断;

6.递推方程:dp[i] = Max(dp[j])+1;其中j的范围是0<=j<i;前提是Va[j]<Va[i];

7.备忘录的制作:这里的备忘录就是一个一位的数组就OK了,不存在滚动数组;

//依然走这七步;

#include <iostream>
#include <algorithm>
#include <cstring>
#define NUM 1005
using namespace std;





int main()
{
    int Va[NUM];//数据数组
    int dp[NUM];//表示储存最长的子序列的个数数组
    int N;//表示数据个数
    cin>>N;
    if(N==0)
        {cout <<0<<endl;return 0;}
    for(int i  = 0;i<N;i++)
        {cin>>Va[i];dp[i] = 1;}
//    for(int i = 0;i<N;i++)
//        cout <<Va[i]<<" ";
//    cout <<endl;//测试
    dp[0] = 1;
        //初始化dp的起点;
    for(int i = 1;i<N;i++){
        for(int j = 0;j<i;j++)
            if(Va[i]>Va[j])//这是满足的前提;
                dp[i] = max(dp[i],dp[j]+1);
    }
    //for(int i = 0;i<N;i++)
      //  cout <<dp[i]<<" ";
   // cout <<endl;//测试

    cout <<*max_element(dp, dp+N)<<endl;

    return 0;
}



点赞