- 题目大意:求最长上升子序列……
- 思路:看这数据规模,L<=100000,还一个点多组测试数据,O(n2)的是不用指望了,于是要使用O(nlogn)的算法。
- 说一下该算法:令序列存于数组a中在O(n2)的算法中,我们对于一个f[i]值的确定,需要扫描j=1->i-1的所有a[j]值,并取a[j]<a[i]时的f[j]的最大值。试想如果存在两个个值x与y,满足x<y<i,x<y,且f[x]=f[y],在构建序列的过程中,我们应该选择x还是y?显然,应该是x,因为在a[x]至a[i]中可能存在一个可以用于构建序列的元素。对于任意的满足f[t]=某一值k的所有a[t],我们记d[k]=min(a[t]),即表示从1至某一位置最长上升子序列长度为k的末尾元素值的最小值,于是可以发现,对于序列d,有d[1]<d[2]<…<d[k-1]<d[k],因为我们保存的都是最小元素值,每增加序列长度,放在最后的元素,因为是上升子序列,必然比原先的末尾元素大。于是我们得出这样一个策略,对于已经求出的长度为len(注意,是已经求出)的序列,对于某个元素a[t],如果a[t]>d[len],只需令a[t]接在d[len]之后,然后len++,即此时序列长度得到增加;否则,在序列d中寻找满足a[t]>d[j]的最大的j,令d[j+1]=a[t],但是因为序列长度没有增加,所以对len无操作。实际操作时,只需枚举序列中的每一个元素,这是O(n)的,在寻找元素插入位置时,由于d具有单调性,我们可以利用二分查找,这是O(logn)的。于是,总时间复杂度降至O(nlogn)。
- 代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxl=100005;
int l,a[maxl],c[maxl];
int find(int l,int r,int x)
{
int mid=(l+r)/2;
if (l==r)
return l;
if (c[mid]>x)
return find(l,mid,x);
else
return find(mid+1,r,x);
}
void work()
{
int len=0,j;
c[1]=-10000;
for (int i=1;i<=l;++i)
{
if (a[i]>c[len])
j=++len;
else j=find(1,len,a[i]);
c[j]=a[i];
}
printf("%d\n",len);
}
void init()
{
while (scanf("%d",&l)==1)
{
for (int i=1;i<=l;++i)
scanf("%d",&a[i]);
work();
}
}
int main()
{
init();
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-12-21 14:10:58