NYOJ17 单调递增最长子序列(最长单调递增子序列)

67 阅读2分钟

题目:

单调递增最长子序列

时间限制: 3000 ms  |  内存限制: 65535 KB

难度: 4

    • 描述

    • 求一个字符串的最长递增子序列的长度
      如:dabdbf最长递增子序列就是abdf,长度为4

        • 输入

        • 第一行一个整数0<n<20,表示有n个字符串要处理
          随后的n行,每行有一个字符串,该字符串的长度不会超过10000

        • 输出

        • 输出字符串的最长递增子序列的长度

        • 样例输入

        • 3
          aaa
          ababc
          abklmncdefg
          
        • 样例输出

        • 1
          3
          7
          
        • 来源

        • 经典题目

        • 上传者

        • iphxer

          \

代码1:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int main()
{
    int n;
    char str[10005];//存储字符串
    int num[10005];//当前元素作为最大元素的最长递增序列
    scanf("%d",&n);
    while(n--)
    {
        mem(str,'\0');
        mem(num,0);//初始化str和num
        int sum=0;
        num[0]=1;//第一个元素的最长递增序列一定为1
        scanf("%s",str);
        int len=strlen(str);

        for(int i=1; i<len; i++)
        {
            int flag=0;//代表num[i]的值
            for(int j=0; j<i; j++)
            {
                if(str[i]>str[j]&&flag<num[j])//把当前的第i个元素和前面的每一个元素比较&&递增序列必须连续递增
                {
                    flag=num[j];//更新序列长度
                }
            }
            num[i]=flag+1;
        }
        for(int i=0; i<len; i++)
        {
            sum=max(sum,num[i]);//找出里面最大的长度
        }
        printf("%d\n",sum);
    }
    return 0;
}

把上面的代码优化一下,可以用逆推的方式这么写

代码2:

#include<stdio.h>  
#include<string.h>  
int dp[10010];  
char a[10010];  
int main()  
{  
    int t;  
    scanf("%d",&t);  
    while(t--)  
    {  
        memset(dp,0,sizeof(dp));  
        int i,la,j,maxn=-1;  
        scanf("%s",a+1);  
        la=strlen(a+1);  
        for(i=1;i<=la;i++)  
            {  
                for(j=i-1;j>=0;j--)  
                {  
                    if(a[j]<a[i]&&dp[i]<dp[j]+1)//判断是否递增,并且判断当前元素所处递增序列的长度  
                        dp[i]=dp[j]+1;//更新递增序列的最大长度  
                }  
                if(dp[i]>maxn)  
                    maxn=dp[i];  
            }  
            printf("%d\n",maxn);  
    }  
    return 0;  
}  


\

\

关于这个问题有一篇博客写的很好:--> 最长递增子序列详解(longest increasing subsequence)

以下内容摘自上述博客
-----------------------------------------------------------------关于单调递增子序列--------------------------------------------------------------------------------

对于动态规划问题,往往存在递推解决方法,这个问题也不例外。要求长度为i的序列的Ai{a1,a2,……,ai}最长递增子序列,需要先求出序列Ai-1{a1,a2,……,ai-1}中以各元素(a1,a2,……,ai-1)作为最大元素的最长递增序列,然后把所有这些递增序列与ai比较,如果某个长度为m序列的末尾元素aj(j<i)比ai要小,则将元素ai加入这个递增子序列,得到一个新的长度为m+1的新序列,否则其长度不变,将处理后的所有i个序列的长度进行比较,其中最长的序列就是所求的最长递增子序列。举例说明,对于序列A{35, 36, 39, 3, 15, 27, 6, 42}当处理到第六个元素(27)时,以35, 36, 39, 3, 15, 27, 6为最末元素的最长递增序列分别为
35
35,36
35,36,39
3
3,15
3,15,27
3,6
当新加入第10个元素42时,这些序列变为
35,42
35,36,42
35,36,39,42,
3,42
3,15,42
3,15,27,42
3,6,42\

这其中最长的递增序列为(35,36,39,42)和(3,15,27,42),所以序列A的最长递增子序列的长度为4,同时在A中长度为4的递增子序列不止一个。

算法的思想十分简单,如果要得出Ai序列的最长递增子序列,就需要计算出Ai-1的所有元素作为最大元素的最长递增序列,依次

递推Ai-2,Ai-3,……,将此过程倒过来,即可得到递推算法,依次推出A1,A2,……,直到推出Ai为止

2018年3月31日20:06:37重写O(N^2)

#include <bits/stdc++.h>
using namespace std;
int dp[10000+100];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int maxx=0;
        string s;
        cin>>s;
        int len=s.length();
        for(int i=0; i<len; i++)
        {
            dp[i]=1;
            for(int j=0; j<i; j++)
            {
                if(s[j]<s[i]&&dp[j]+1>dp[i])
                {
                    dp[i]=dp[j]+1;
                }
            }
            maxx=max(maxx,dp[i]);
        }
        cout<<maxx<<endl;
    }
    return 0;
}


\