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

题解;这道题并不是简单的求最长上升子序列,而是需要包含一个给定的数,其实并不难。我们考虑一下,既然一定要包含这个数,那么这个数前面比它大的数就不能用,这个数后面比它小的数也不能用,所以我们可以先删去这个数前面比它大的数和这个数后面比它小的数,然后再找最长上升子序列,这个题需要O(nlogn)的复杂度,所以需要用二分查找来做,具体做法可以去看我的另一篇博客codevs 2188 最长严格上升子序列(加强版)(DP)

代码如下

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int inf=10100000;
int dp[200100],a[200100],b[200100];
int main()
{
    int n,k,tot=0,ans=0;
    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]) b[++tot]=a[i];//删去它前面比它大的 
    for(int i=k+1;i<=n;i++)
        if(a[i]>a[k]) b[++tot]=a[i];//删去它后面比它小的 
    memset(dp,63,sizeof(dp));
    for(int i=1;i<=n;i++)
        dp[lower_bound(dp+1,dp+n+1,b[i])-dp]=b[i];//求最长上升子序列 
    printf("%d\n",(lower_bound(dp+1,dp+n+1,inf)-dp));
    return 0;
}
发布了81 篇原创文章 · 获赞 2 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Loi_YZS/article/details/53013140