最长上升子序列[模板]

最长上升子序列就是指在一个序列中,求出一个最长的子序列满足单调递增,同时各个元素在原序列中不要求连续。


这个方法很巧妙,不妨进行如下考虑:对于序列中的元素A[x]

首先有,B[1]=A[1]=4且len=1。

接着考虑A[2],有B[1]=2,len=1,对于A[3],因为A[3]>B[len],所以有len=len+1,B[len]=6

考虑A[4],可有B[2]=3,考虑A[5],无法插入数组B,考虑A[6],有len=len+1,B[len]=5。

故最终len=3,B[i]={2,3,5}。


其核心代码是:


scanf("%d",&T);
for(int kase=1;kase<=T;kase++)
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    b[1]=a[1];
    len=1;
    for(int i=2;i<=n;i++)
        if(a[i]>b[len]) b[++len]=a[i];
        else
        {
            int pos=lower_bound(b+1,b+1+len,a[i])-b;
            b[pos]=a[i];
        }
    printf("%d\n",len);
}

一道例题如下:


A序列

发布时间: 2017年7月9日 20:20   最后更新: 2017年7月10日 21:11   时间限制: 1000ms   内存限制: 128M

如果一个序列有奇数个正整数组成,不妨令此序列为a1,a2,a3,...,a2k+1(0<=k),并且a1,a2...ak+1是一个严格递增的序列,ak+1,ak+2,...,a2k+1,是一个严格递减的序列,则称此序列是A序列。

比如1 2 5 4 3就是一个A序列。

现在Jazz有一个长度为n的数组,他希望让你求出这个数组所有满足A序列定义的子序列里面最大的那个长度。(子序列可以不连续)

比如1 2 5 4 3 6 7 8 9,最长的A序列子串是1 2 5 4 3。

多组输入,每组两行。
第一行是n,表示给的数组的长度。
第二行有n个数(int范围),即给你的数组。

1<=n<=500000

每组输入输出一行,即最长的A序列子串的长度。

 复制
9
1 2 5 4 3 6 7 8 9
5
思路:将这个序列中的中间那个点枚举出来,算出这个点左边的最长上升子序列以及这个点右边(包括这个点)的最

长下降子序列,取这二者的最小值,然后再将该值乘以二减一得到每个点对应的A序列。然后遍历这个序列,将每一

个点这样扫一遍即可。

最长上升子序列和最长下降子序列可以使用模板,这里就不详细说明了。


下面是ac代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<iomanip>
#define ll long long
#define INF 0x3f3f3f3f
#define mod 1e9+7
#define clr(a,x) memset(a,x,sizeof(a))
using namespace std;
const double eps = 1e-6;
#define N 500001
int num[N],temp[N],ans[N],l[N],r[N];
int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&num[i]);
        }
        int Max=1;
        clr(temp,INF);
        temp[0]=-1;
        for(int i=1;i<=n;i++)
        {
            int pos=lower_bound(temp,temp+n,num[i])-temp;
            temp[pos]=num[i];
            Max=max(Max,pos);
            l[i]=Max;
        }
        Max=1;
        clr(temp,INF);
        temp[0]=-1;
        for(int i=n;i>=1;i--)
        {
            int pos=lower_bound(temp,temp+n,num[i])-temp;
            temp[pos]=num[i];
            Max=max(Max,pos);
            r[i]=Max;
        }
        int ans=1;
        for(int i=1;i<=n;i++)
        {
            ans=max(ans,min(l[i],r[i]));
        }
        printf("%d\n",ans*2-1);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值