【郭炜老师】【动态规划】2.最长上升子序列OpenJudge 2757

最长上升子序列定义:

          给定一个数列(a1,a2,a3,,,an),当a1<a2<a3<...<an时我们称这个数列时上升数列。

          若给定一组数(a1,a2,a3,----an),存在一组数(ai1,ai2,,,aik)是一个上升序列,(其中1<=ai1<ai2<....<aik <=n),那么称(ai1,ai2,,,aik)是这组数的一个上升序列,如(1,7,3,5,9,4,8)的其中一个上升序列是(1,3),(3,4,8),而这些上升序列的个数的最大值就是“ 最长上升子序列 ”,如(1,3,4,8)是一组最长上升子序列,他的长度是4。

 

OpenJudge2757最长上升子序列:

题目要求先输入一个序列,然后且最长上升子序列的个数最大值。

Input:

7
1 7 3 5 9 4 8

Output:

4

思路介绍:

          这是一道动规题。以下讨论中我们用ak代表数组a的第k个元素。(即k是数组a的下标)

         1.先找子问题:

                   解法一:”求序列的前n个元素的最长上升子序列长度“,是一种办法。

                    (但是这个办法不具有”无后效型“,即第n+1个元素插入后,对前n个元素的最长上升子序列长度有较大影响,可能              是最长的,也可能不是。故排除)。

                    解法二:”求以ak(k = 1,2,3,,,,N)为终点的最长上升子序列的长度,“,本子问题虽然与原问题不同,但是有“无后效                性”,每个点得出的结果是确定的。然后对N个结果找最大值即是所求。

          2.确定状态:

                    一共有N个点,每个点代表着以该点为终点的最长上升子序列的长度,因此,共有N种状态,即每个ak值就是该状态。(这里假设a数组是存放以每个数为节点的最长上升子序列的长度。k即第k个点)。

          3.找出状态转换方程:

                    以maxLen[ i ] 代表以ak作为终点的最长上升子序列的长度。

                    ①。初始状态maxLen[ k ] = 1;

                    ②。maxLen [ k ] = max{maxLen( i ) : 1<= i <k 且ai < ak  且  k!=1)}+1 

                    (maxLen(n)的值,就是在ak左边,终点数值小于ak,且长度最大的上升子序列的长度加1.(因为在ak左边任何终点小于ak的自I序列,加上ak偶就能形成一个更长的上升子序列))

具体做法:

          1.首先给出一个数组a存放这几个数,然后给出一个数组maxLen,代表以ai为终点的最长上升子序列的长度。

const int maxn = 1010;
int a[maxn];
int maxLen[maxn];

          2.然后输入所有的数,同时还要对每个maxLen[ i ]赋值为1,代表以ai为“终点”的最长上升子序列的长度。初始值为1,即本身的长度。

for(int i=1;i<=n;i++){
    cin >> a[i];
    maxLen[i] = 1;
}

          3.   第一个数最长上升子序列就是1,不用变。然后从2开始(即第 i 位),找第2位左边的a [ 2]的值大于它的且 maxLen[ j ]的值加一大于它的最大值的这个数。 (一共三个条件),(如果不好理解,请看下面的推导过程,建议下载下来看。)

/*用到了max函数,需要#include <algorithm>  */

for(int i=2;i<=n;i++)
{
    for(int j=1;j<i;j++)
    {
        maxLen[i] = max(maxLen[i],maxLen[j]+1);
    }
}

4.然后找出maxLen当中的最大者,即是最长上升子序列的长度。

cout << *max_element(a+1,a+n+1) << endl;

推导过程:(我给了一个图进行模拟。)(建议下载图片推导一遍)

 

时间复杂度讨论:

          dp的时间复杂度 = 状态数 × 求状态所需时间。

          显然N个状态,求每个是N次比较,故时间复杂度为O(N²)

代码:

#include <iostream>
#include <algorithm>
using namespace std;
/*OpenJudge 2527*/
const int MAXN = 1010;
int a[MAXN];
int maxLen[MAXN];

int main()
{
    int N;
    cin >> N;
    for(int i=1;i<=N;i++)
    {
        cin >> a[i];
        maxLen[i] = 1;    //注1:记住要初始化。
    }
    for(int i=2;i<=N;i++)
    //每次求以第i个数为终点的最长上升子序列的长度
    {
        for(int j=1;j<i;j++)
        //查看以第j个数为终点的最长上升子序列
            if( a[i] > a[j])
                maxLen[i] = max(maxLen[i],maxLen[j]+1);

    }
    cout << *max_element(maxLen+1,maxLen+N+1);
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值