上升子序列問題


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

 注:本解法有誤,反例已在評論中指出


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM