zoukankan      html  css  js  c++  java
  • codevs 1576 最长严格上升子序列

    题目链接:http://codevs.cn/problem/1576/
    题目描述 Description

    给一个数组a1, a2 ... an,找到最长的上升降子序列ab1<ab2< .. <abk,其中b1<b2<..bk。

    输出长度即可。

    输入描述 Input Description

    第一行,一个整数N。

    第二行 ,N个整数(N < = 5000)

    输出描述 Output Description

    输出K的极大值,即最长不下降子序列的长度

    样例输入 Sample Input

    5

    9 3 6 2 7

    样例输出 Sample Output

    3

    数据范围及提示 Data Size & Hint

    【样例解释】

    最长不下降子序列为3,6,7

    解题思路

    参考:北大郭炜老师

    1.找子问题:“求以ak( k=1, 2, 3…N)为终点的最长上升子序列的长度”
    一个上升子序列中最右边的那个数,称为该子序列的“终点”。
    虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。

    2. 确定状态
    子问题只和一个变量-- 数字的位置相关。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以a[k]做为“终点”的最长上升子序列的长度。状态一共有N个。

    3. 找出状态转移方程

    maxLen [k]表示以a[k]做为“终点”的最长上升子序列的长度那么:
    初始状态: maxLen [1] = 1
    maxLen[k]= max { maxLen [i]: 1<=i < k 且 a[i ]< a[k]且 k≠1 } + 1
    若找不到这样的i,则maxLen[k] = 1

    maxLen[k]的值,就是在a[k]左边,“终点”数值小于a[k] ,且长度最大的那个上升子序列的长度再加1。因为a[k]左边任何“终点”小于a[k]的子序列,加上a[k]后就能形成一个更长的上升子序列 。

     1 #include <stdio.h>
     2 #define maxN 5005
     3 int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做为“终点”的最长上升子序列的长度
     4 int main(int argc, char *argv[])
     5 {
     6     int i,j;
     7     scanf("%d",&n);
     8     for(i=0;i<n;i++) {  scanf("%d",&a[i]); maxLen[i]=1;  }
     9     
    10     for(i=1;i<n;i++)//枚举所有子序列的终点 
    11     {
    12         for(j=0;j<i;j++)//枚举以a[i]做终点的子序列中a[i]的前缀元素 
    13         {
    14             if(a[j]<a[i])//尝试用a[j]做a[i]的直接前缀形成新的子序列 
    15             {
    16                 maxLen[i]=(maxLen[j]+1>maxLen[i]?maxLen[j]+1:maxLen[i]);
    17             }
    18         }
    19     }
    20     printf("%d
    ",maxLen[n-1]);
    21     return 0;
    22 }

    上面的代码写错了,抱歉。更正如下:

     1 #include <stdio.h>
     2 #define maxN 5005
     3 int main(int argc, char *argv[])
     4 {
     5     int i,j,t;
     6     int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做为“终点”的最长上升子序列的长度
     7     int max;
     8     
     9     scanf("%d",&n);
    10     for(i=0;i<n;i++) {  scanf("%d",&a[i]); maxLen[i]=1;  }
    11     for(i=1;i<n;i++)//枚举所有子序列的终点 
    12     {
    13         for(j=0;j<i;j++)//枚举以a[i]做终点的子序列中a[i]的前缀元素 
    14         {
    15             if(a[j]<a[i])//尝试用a[j]做a[i]的直接前缀形成新的子序列 
    16             {
    17                 maxLen[i]=(maxLen[j]+1>maxLen[i]?maxLen[j]+1:maxLen[i]);
    18             }
    19         }
    20     }
    21     max=maxLen[0];
    22     for(i=1;i<n;i++)
    23         if(maxLen[i]>max) max=maxLen[i];
    24     printf("%d
    ",max);
    25     return 0;
    26 }

    思考题 : 如何改进程序,使之能够输出最长上升子序列 ?

    思路:新增pre[ ],其中pre[k]=x表示在a[ ]序列构成的若干个上升子序列中,a[k]的前驱是a[x]。一开始pre[ ]全部初始化为-1表示一开始所有元素的前驱都是自己本身。在循环求解maxLen[i]的同时,更新pre[i]。最后在扫描出maxLen[ ]最大值为maxLen[i]以后,从pre[i]往前推即可。假如要顺序输出该最长上升子序列,可以把逆推pre[ ]的过程保存再输出。

    参考代码:

     1 #include<stdio.h>
     2 #include<string.h>
     3 #define maxN 5005
     4 int main(int argc, char *argv[])
     5 {
     6     int i,j,t;
     7     int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做为“终点”的最长上升子序列的长度
     8     int max;
     9     int pre[maxN];
    10     int c[maxN],maxIndex;
    11     
    12     memset(pre,-1,sizeof(pre));
    13     
    14     scanf("%d",&n);
    15     for(i=0;i<n;i++) {  scanf("%d",&a[i]); maxLen[i]=1;  }
    16     
    17     for(i=1;i<n;i++)//枚举所有子序列的终点 
    18     {
    19         for(j=0;j<i;j++)//枚举以a[i]做终点的子序列中a[i]的前缀元素 
    20         {
    21             if(a[j]<a[i])//尝试用a[j]做a[i]的直接前缀形成新的子序列 
    22             {
    23                 if(maxLen[j]+1>maxLen[i])
    24                 {
    25                     maxLen[i]=maxLen[j]+1;
    26                     pre[i]=j;
    27                 }
    28             }
    29         }
    30     }
    31     max=maxLen[0];
    32     for(i=1;i<n;i++)
    33         if(maxLen[i]>max) { max=maxLen[i]; maxIndex=i; }
    34     printf("%d
    ",max);
    35     
    36     j=0;
    37     c[j++]=a[maxIndex];
    38     while(pre[maxIndex]!=-1)
    39     {
    40         maxIndex=pre[maxIndex];
    41         c[j++]=a[maxIndex];
    42     }
    43     for(i=j-1;i>=0;i--)
    44     {
    45         printf("%d ",c[i]);
    46     }
    47     printf("
    ");
    48     return 0;
    49 }
    View Code

    输出最长上升子序列的另一种思路:

     1 #include <stdio.h>
     2 int n,size,a[1005][5],s,ans,next;
     3 int main(int argc, char *argv[])
     4 {
     5     scanf("%d",&n);
     6     for(int i=0;i<n;i++)
     7     {
     8         int t;
     9         scanf("%d",&t);
    10         a[i][0]=t;a[i][1]=1;a[i][2]=0;
    11         //a[i][1]表示以a[i][0]开头的最长上升子序列的长度。
    12         //a[i][2]表示在以a[i][0]开头的最长上升子序列中a[i][0]的下一个数在原序列中的下标。
    13     }
    14     
    15     for(int i=n-2;i>=0;i--)
    16     {
    17         size=next=0;
    18         for(int j=i+1;j<n;j++)
    19             if(a[j][0]>a[i][0]&&a[j][1]>size) {size=a[j][1];next=j;}
    20         if(size>0) {a[i][1]=size+1;a[i][2]=next;}
    21     }
    22     
    23     ans=0;
    24     for(int i=0;i<n;i++)
    25         if(a[i][1]>a[ans][1]) ans=i;
    26         
    27     printf("%d
    ",a[ans][1]);
    28     
    29     /*for(int i=0;i<n;i++)
    30         printf("%d %d %d %d
    ",a[i][0],a[i][1],a[i][2],i);*/
    31     
    32     int i=ans;
    33     while(a[i][2]>0)
    34     {
    35         printf("%d ",a[i][0]);
    36         i=a[i][2];
    37     }
    38     printf("%d
    ",a[i][0]);
    39     return 0;
    40 }
    View Code

    测试OJ地址:

    http://noi.openjudge.cn/ch0206/1759/

    http://bailian.openjudge.cn/practice/2757/

  • 相关阅读:
    2.7连接数据库中遇见的相应问题1
    linux bash中too many arguments问题的解决方法
    linux系统补丁更新 yum命令
    安装node,linux升级gcc
    python-导出Jenkins任务
    升级openssl和openssh版本
    linux修改文件所属的用户组以及用户
    linux的Umask 为022 和027 都是什么意思?
    keepalived
    自己编写k8s
  • 原文地址:https://www.cnblogs.com/huashanqingzhu/p/7326739.html
Copyright © 2011-2022 走看看