zoukankan      html  css  js  c++  java
  • 上升子序列问题

    T1朴素的最长严格上升子序列

    http://codevs.cn/problem/3955/

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

    输出长度即可。

    输入描述 Input Description

    第一行,一个整数N。

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

    输出描述 Output Description

    输出K的极大值,即最长严格上升子序列的长度

    样例输入 Sample Input

    5

    9 3 6 2 7

    样例输出 Sample Output

    3

    n²做法:f[i]=j表示以第i个数结尾的最长上升子序列长度为j

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,a[5001],f[5001],ans;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            int maxn=0;
            for(int j=1;j<i;j++) 
             if(a[i]>a[j]&&maxn<f[j]) 
              maxn=f[j];
            f[i]=maxn+1;
            ans=max(f[i],ans);
        }
        printf("%d",ans);
    }
    View Code

    nlogn做法:f[i]=表示长度为i的最长上升子序列中,第i个数最小是j,二分查找

    #include<cstdio>
    using namespace std;
    int n,f[5001],s,x;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            if(x>f[s]) 
            {
                f[++s]=x;
                continue;
            }
            int l=0,r=s,k;
            while(l<=r)
            {
                int m=l+r>>1;
                if(x>f[m]) 
                {
                    l=m+1;    k=m;
                }
                else r=m-1;
            }
            if(f[k+1]>x) f[k+1]=x;
        }
        printf("%d",s);
    }
    View Code

    T2 包含第k个数的最长上升子序列

     http://codevs.cn/problem/2188/

    题目描述 Description

    LIS问题是最经典的动态规划基础问题之一。如果要求一个满足一定条件的最长上升子序列,你还能解决吗?

        给出一个长度为N整数序列,请求出它的包含第K个元素的最长上升子序列。

        例如:对于长度为6的序列<2,7,3,4,8,5>,它的最长上升子序列为<2,3,4,5>,但如果限制一定要包含第2个元素,那么满足此要求的最长上升子序列就只能是<2,7,8>了。

    输入描述 Input Description

    第一行为两个整数N,K,如上所述。

        接下来是N个整数,描述一个序列。

    输出描述 Output Description

    请输出两个整数,即包含第K个元素的最长上升子序列长度。

    样例输入 Sample Input

    8 6

    65 158 170 299 300 155 207 389

    样例输出 Sample Output

    4

    数据范围及提示 Data Size & Hint

    80%的数据,满足0<n<=1000,0<k<=n

        100%的数据,满足0<n<=200000,0<k<=n

    把k前面大于等于第k个数的都删去,k后面小于等于第k个数的都删去,然后套上面的方法

    #include<cstdio>
    using namespace std;
    int n,a[200001],f[200001],s,k,x;
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<k;i++) 
         if(a[i]>=a[k]) a[i]=-1;
        for(int i=1;i<=n;i++)
        {
            if(a[i]==-1) continue;
            if(i>k&&a[i]<=a[k]) continue;
            if(a[i]>f[s]) 
            {
                f[++s]=a[i];
                continue;
            }
            int l=0,r=s,p=0;
            while(l<=r)
            {
                int m=l+r>>1;
                if(a[i]>f[m]) 
                {
                    l=m+1;    p=m;
                }
                else r=m-1;
            }
            if(f[p+1]>a[i]) f[p+1]=a[i];
        }
        printf("%d",s);
    }
    View Code

    两个错误:

    1、基本思路错误。错误代码:

    //f[i][j]只会在j时更新,而在j后面与j形成上升子序列的状态不会使j更新。即f[i][j]只对j及其前面的答案正确。故此代码错误
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,a[1001],f[1001][1001],k,ans;
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            int maxn=0;
            for(int j=1;j<i;j++) 
             if(a[i]>a[j]) 
              {
                   maxn=max(maxn,f[j][0]);
                 f[i][j]=f[j][0]+1;
              }
            f[i][0]=f[i][i]=maxn+1;
        }
        for(int i=1;i<=n;i++)
         ans=max(ans,f[i][k]);
        printf("%d",ans);
    }
    View Code

    2、二分查找中p没有赋初值0

    T3  最长上升子序列划分

    http://codevs.cn/problem/4197/

    题目描述 Description

    给定一个长度为N(N为偶数)的序列,问能否将其划分为两个长度为N/2的严格递增子序列。

    输入描述 Input Description

    若干行,每行表示一组数据。对于每组数据,首先输入一个整数N,表示序列的长度。之后N个整数表示这个序列。

    输出描述 Output Description

    同输入行数。对于每组数据,如果存在一种划分,则输出“Yes!”,否则输出“No!“。 

    样例输入 Sample Input

    6 3 1 4 5 8 7

    6 3 2 1 6 5 4

    样例输出 Sample Output

    Yes!

    No!

    数据范围及提示 Data Size & Hint

    共两组数据,每组数据行数<=50,0 <= 输入的所有数 <= 10^9

    第一组(30%):N <= 20

    第二组(70%):N <= 2000

    小提示:输入部分可以用while(scanf(“%d”,&n)!=EOF)或者while not eof do read(n)

    由观察可以发现,若满足此条件,则序列最长上升子序列的长度<=2

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,a[2001],f[2001],p;
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
           for(int i=1;i<=n;i++) scanf("%d",&a[i]);
           p=0;
           memset(f,0,sizeof(f));
            for(int i=1;i<=n;i++)
             {
                 int maxn=0;
                 for(int j=1;j<i;j++)
                 if(a[i]<=a[j])
                  maxn=max(maxn,f[j]);
                f[i]=maxn+1;
             }
            for(int i=1;i<=n;i++) p=max(p,f[i]);
            if(p>2) printf("No!
    ");
            else printf("Yes!
    ");
        }
    
    }
    View Code

     注:本解法有误,反例已在评论中指出

  • 相关阅读:
    面向对象程序设计六大原则
    获取View的快照
    Xcode调试之exc_bad_access以及 message sent to deallocated instance
    OOA/OOD/OOP
    检测网络是否有效
    图像IO
    iOS应用性能调优的25个建议和技巧
    iOS围绕某点缩放或旋转的AnchorPoint的设定
    NPOI导出Excel2007板
    不同三级域名与二级域名之间互相共享Cookie
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6358278.html
Copyright © 2011-2022 走看看