2013 多校第一场 hdu 4604 Deque

hdu 4604

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4604

题目大意:给你一个序列和一个双端队列,维持队列里是不下降的,问你一个一个加序列里的元素,队列的最大的长度是多少。

思路:首先,只考虑一个单调上升的双端队列,那么对于序列 A 和 队列 Q,找出以 Ai 为首的最长上升子序列和最长下降子序列,那么 Q 能维持的最大长度为这两个长度之和。现在,题目中的元素是可以重复的,那么,先求出以 Ai 为首的最长不下降子序列和最长不上升子序列,那么 Q 能维持的最大长度 = 这两个的长度之和 - 这两个序列中 Ai 个数的最小值。

以上都是解题报告里说的,自己比赛做的时候,因为情况太多了,进来又出去的,选了某个,又影响后面的,脑子很乱,根本没想到这一点,但是的确脑子里有出现过求最长递增子序列,没有深究下去,也没有发现他这个双端队列两端能进出的本质吧。。同时,有了思路,这道题最关键的地方还是在于判重,要深入理解LIS O(nlogn)这个算法的过程,其实当当前处理元素为 Ai 时,找到它的 pos ,那么d[1]~d[ pos ] 这里面存的就是以它为结尾的最长序列,再分别二分求出这两个序列中 Ai 的个数就行了。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff ;

const int MAXN = 100011 ;

int n;

int a[MAXN],d[2][MAXN];

int tot0,tot1;

int ans;

void lis()
{
    d[0][1]=a[n];
    tot0=1;
    d[1][1]=a[n];
    tot1=1;
    for(int i=n-1;i>=1;i--)
    {
        int pos0;
        if(a[i]>=d[0][tot0])
        {
            tot0++;
            d[0][tot0]=a[i];
            pos0 = tot0;
        }
        else
        {
            pos0 = upper_bound(d[0]+1,d[0]+tot0+1,a[i])-d[0];
            d[0][pos0] = a[i];
        }

        int pos1;
        if(a[i]<=d[1][tot1])
        {
            tot1++;
            d[1][tot1] = a[i];
            pos1  = tot1;
        }
        else
        {
            int l = 1,r = tot1;
            while(l<r)
            {
                int m = l+r>>1;
                if(d[1][m]>=a[i])
                {
                    l=m+1;
                }
                else
                {
                    r = m;
                }
            }
            pos1 = l;
            d[1][pos1] = a[i];
        }
        int f0 = pos0-(lower_bound(d[0]+1,d[0]+pos0+1,a[i])-d[0])+1;
        int l = 1,r = pos1;
        while(l<r)
        {
            int m = l+r>>1;
            if(d[1][m]==a[i])
            {
                r= m;
            }
            else if(d[1][m]>a[i])
                l = m+1;
            else r = m-1;
        }
        int f1 = pos1 - l+1;
        //printf("pos0 = %d,pos1 = %d,f0 = %d,f1 = %d\n",pos0,pos1,f0,f1);
        ans = max(ans,pos0+pos1-min(f0,f1));
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        ans=1;
        lis();
        printf("%d\n",ans);
    }
    return 0;
}
/*
10
1 2 1 3 2 1 3 1 2 2
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值