再谈升/降序子序列——POJ1631

本文探讨了如何将一个序列划分成最少数量的单调子序列,并提供了一种贪心算法实现方案,通过队列操作来确定最少的子序列个数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章介绍了如何求解最长升/降序子序列的长度,这篇文章讨论另一个与升/降序子序列有关的问题。

问题:将一个序列划分成单调的子序列最少可划分成多少个?

例如。序列:1、4、2最少可划分成2个单调递增的子序列:{1,2}、{4}或{1,4}、{2};

乍一看,觉得这需要动规,其实这可以贪心。

在用动规求序列{1、4、2}的时候,我们要考虑的一个重要问题是,1是与4连接还是与2连接,

因为这种选择会影响到后面我们能获得的子序列的长度。但是现在我们并不需要求最长子序列

的长度是多少,我们要求的是序列的个数。

其实1与4连接或与2连接都会得到同样多的子序列,所以我们可以设计一个如下的算法:

queue q;

for(i:序的第一元素 to 序列的最后一个元素) {

for(k:队列的一个元素 to 队列的最后一个元素) 

if(队列元素k<序列元素i)  { k=i; break(); }

if(队列中没有比当前元素小的元素) 将当前元素插入队列;

}

最后队列的长度值就是子序列的个数。(使用二分后时间复杂度为O(nlgn))


POJ 1631:http://poj.org/problem?id=1631


#include<cstdio>
#include<algorithm>

using namespace std;

int arr[100100],marr[100100];

int bs(int s,int e,int k)
{
    int mid=(s+e)>>1;
    if(marr[mid]<=k && mid>=e) return mid;
    if(marr[mid]<=k && k<marr[mid+1]) return mid;
    if(k<marr[mid]) return bs(s,mid-1,k);
    else return bs(mid+1,e,k);
}

int main()
{
    //freopen("1631.in","r",stdin);
    int t,p;
    scanf("%d",&t);
    while(t--) {
         scanf("%d",&p);
         for(int i=1;i<=p;i++) scanf("%d",&arr[i]);
         marr[0]=arr[1];
         for(int i=1;i<=p;i++) if(arr[i]<marr[0]) marr[0]=arr[i]-1;
         int len=0;
         for(int i=1;i<=p;i++) {
              int idx=bs(0,len,arr[i]);
              marr[idx+1]=arr[i];
              if(idx+1>len) len=idx+1;
         }
         printf("%d\n",len);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值