上升子序列问题

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!\n");
        else printf("Yes!\n");
    }

}
View Code

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

posted @ 2017-01-31 06:19  TRTTG  阅读(1295)  评论(7编辑  收藏  举报