Codevs 2188 最长上升子序列 - 序列DP

传送门

题目描述 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个元素的最长上升子序列长度,因此最长上升子序列长度dp[n]必须由dp[k]转移来。

AC Code:

80分 O(n^2)做法

DP:

#include<cstdio>
#include<algorithm>
using namespace std;
const int sz=200000+10;
int a[sz];
int dp[sz];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int ans=-99;
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[j]<a[i])
            {
                if(j<k&&a[j]<a[k])
                    dp[i]=max(dp[i],dp[j]+1);
                else if(j==k)
                    dp[i]+=1;
                else if(j>k&&a[j]>a[k])
                    dp[i]=max(dp[i],dp[j]+1);
            }    
        }
        ans=max(ans,dp[i]);
    }
    printf("%d",ans);
    return 0;
}

记忆化搜索:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
int a[5050];
int dp[5050];
int dfs(int x) {
    if(dp[x]) return dp[x];
    if(x==n+1) return 0;
    dp[x]=0;//因为是“接下来”最长的上升子序列长度 因此dp[x]=0 
    for(int i=x+1;i<=n;i++) {
        if(a[i]>a[x]) 
            dp[x]=max(dp[x],dfs(i)+1);
    }
    return dp[x];
}
int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
    }
    int ans=-(1<<30);
    for(int i=1;i<=n;i++) ans=max(ans,dfs(i)+1);
    //最长上升子序列不一定是从a[1]开始  
    printf("%d",ans);
    return 0;
}

 

100分 O(nlogn)做法

 

 

 

 

 

 

posted @ 2018-01-20 08:17  dprswdr  阅读(182)  评论(0)    收藏  举报