zoukankan      html  css  js  c++  java
  • 最长上升子序列

    【基础算法】最长上升子序列

    时间限制: 1 Sec  内存限制: 64 MB

    题目描述

    给定一个整数序列A1A2A3….An。求它的一个递增子序列,使子序列的元素个数尽量多,元素不一定要求连续。

    输入

    第1行:1个整数n(1<=n<=5000),表示序列中元素的个数.

    第2行-n+1行:每行1个整数x(-1000<=x<=1000),第i+1行表示序列中的第i个元素。

    输出

    第1行:1个整数k,表示最长上升子序列的长度。

    第2行:k个用单个空格分开的整数,表示找到了最长上升子序列。如果有多个长度等于k的子序列,则输出最靠前的1个。

    样例输入

    8
    1
    3
    2
    4
    3
    5
    4
    6
    

    样例输出

    5
    1 3 4 5 6
    #--------------------------------------------------------------------------------#
    最基础的动规题~就让我来详细地为小白们讲解一下吧

    首先,讲解一下题目:
    给你一个序列(就一串数),你可以理解为几个人。
    这几个人身高各不相同(有的很矮(两三厘米什么的不要在意),有的很高(也就一两千厘米))。
    现在你要在这几个人当中找出一些人做苦力,你需要使揪出来的人的身高从低到高(不改变顺序的情况下)排列。
    当然,我们都希望人越多越好~就是让你算出最多有多少,并输出他们的编号。

    再解释一下样例:
    八个数:1 3 2 4 3 5 4 6
    这几个人的身高……呵呵,要抓出几个来,使身高为从低到高排列,例如:
    1 4 5 6
    我们抓的就是①、④、⑥和⑧号犯人,这样就是从低到高了。
    但是,这样是不是最多的呢,显然不是,最多的是:
    1 3 4 5 6
    就抓了①、②、④、⑥和⑧号犯人,他们就很荣幸地成为了苦力。

    这里有一个细节——升高一样是不能在一起的,你可以理解为两个身高一样的在一起,我说我高,你说你高,于是便打起架来了……

    当然,这只是针对于“最大上升子序列”,其他的也是一样,只是排列方式不同而已,如“最大不上升子序列”,就是抓出一堆人,使身高从高到低排列(可以一样(大概是他们不敢打架吧,不要在意细节))。

    #--------------------------------------------------------------------------------#

    好的题目解释完了,怎么做呢?
    啊啊哈哈哈,当然是直接选就行了啊,专业名词——贪心
    好的,很不幸,用贪心是不行的……

    这里就要用一种新东西——动态规划(简称动规)

    这种东西到底是什么呢,
    总之,这是一种递推的思想,你需要将当前的状态(原谅我我找不出更好的词)用之前的状态表示出来,最后只需一个循环,你就可以通过f[1]得到f[n](通常是指最优解问题)。

    好的,对于这道题,我们可以列出一个方程(且称这猥琐的东西为方程)

    f[i]=max{f[1],f[2],...,f[i-1]}+1


    这是什么意思呢?首先:

    f[i]表示的就是第i个人(必须要有第i个人)之前最多可以抓多少个苦力,显然,f[1]=1,因为第1个人之前没有人比TA高……

    max应该不陌生,就是取f[1],f[2],...,f[i-1]中最大的。

    但是,如果你明白了一些,肯定会问:第i个人不一定比1-(i-1)人都要高啊?

    所以,还是让j从1到i-1开始枚举f[j],但是,在选最大的时候要再加一个条件:a[j]<a[i],a是存身高(原始序列)的数组。

    但是,a[j]<a[i]只是表示第j个人比第i个人小,不表示f[j]中的每一个人都比i要矮啊?不用担心,既然这个人已经进了f[j],TA就一定比j要矮,而j比i矮,所以,f[j]中包含的每一个人都会比i要矮。

    于是就有了:if(f[j]>maxi&&a[j]<a[i]) maxi=f[j],这样就选出来了。

    为什么最后还要加1呢?看看前面:“f[i]表示的就是第i个人(必须要有第i个人)”,所以,算上i,还要再加1。

    关于求最多苦力就是这样了。


    接下来还有,要输出苦力的编号:

    在找maxi的时候,就把j记录下来,最后递归输出,这部分详情看代码。


    有问题欢迎留言~

    代码:

    #include<cstdio>
    #include<cstring>
    int a[5005],n,maxans,flag=1;//a为原始数组,n是人数量,maxans表示最多的苦力在f中的哪个位置
    int f[5005],ans[5005];//f是动规数组,ans是存最多苦力的编号的数组
    void print(int x)
    {
        if(ans[x]!=0)//直到父亲为0(没有了)
            print(ans[x]);
        if(flag) printf("%d",a[x]),flag=0;
        else printf(" %d",a[x]);//flag控制多余空格
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);//读入
        for(int i=1;i<=n;i++)
        {
            int maxi=0;//寻找f[1],f[2],...,f[i-1]中最大且符合条件的一个
            for(int j=1;j<i;j++)
                if(f[j]>maxi&&a[j]<a[i])
                {
                    maxi=f[j];
                    ans[i]=j;//可以理解为i的父亲(就是i是由j推来的)
                }
            f[i]=maxi+1;
            if(f[i]>f[maxans])//找最大的
                maxans=i;
        }
        printf("%d
    ",f[maxans]);
        print(maxans);
    }
                                                                                                                                                                   By WZY


  • 相关阅读:
    showModalDialog 超过问题
    工作流,WEB框架,UI组件网络收集整理
    VSS使用方法详解
    Windows远程桌面连接命令mstsc
    Nginx 配置简述
    jQuery编程代码规范的最佳实践
    TinyMCE(富文本编辑器)在Asp.Net中的使用方法
    扣文转文方法
    VS2013 EMMET插件学习
    为革命保护视力 --- 给 Visual Studio 换颜色
  • 原文地址:https://www.cnblogs.com/LinqiongTaoist/p/7203737.html
Copyright © 2011-2022 走看看