上一篇文章介绍了如何求解最长升/降序子序列的长度,这篇文章讨论另一个与升/降序子序列有关的问题。
问题:将一个序列划分成单调的子序列最少可划分成多少个?
例如。序列: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;
}
本文探讨了如何将一个序列划分成最少数量的单调子序列,并提供了一种贪心算法实现方案,通过队列操作来确定最少的子序列个数。
1312

被折叠的 条评论
为什么被折叠?



