【题目链接】
http://acm.hdu.edu.cn/showproblem.php?pid=5489
【解题报告】
题目大意是,在数组A中,删去连续的L个数之后的最长上升子序列(LIS)。
为了解决这道题目,我们先来看LIS的nlogn求法:
1.维护一个单调递增序列LIS
2.对每个点A[i],找到LIS里大于等于它的第一个位置,尝试把它插入到序列里第x个位置。(二分查找)
那么dp[i]表示0..i的序列,以i为末位的最长上升子序列。显然dp[i]=x
3.如果A[i]<LIS[x]那么把LIS[x]更新为A[i]继续向后更新。
4.ans=max{ dp[i] }
这个算法的时间复杂度显而易见是O( nlogn )的。
之后来看这道题目。这时候算法已经很清楚了。
对每个i,我们求把它放在0…i-L这个序列后的LIS,这个维护方法是,每次我们扫到一个a[i],把a[i-L]插入到LIS里。
这时候我们还需要求i..N的LIS,这个很简单,倒过来求一次LIS即可。
最后我们求得每个i的LIS,取最大值即可。
时间复杂度是O( nlogn )。
【参考代码】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int INF=1e9;
const int maxn=1e5+50;
int N,L;
int a[maxn],b[maxn];
int LIS[maxn];
int dp[maxn];
int main()
{
int T,kase=0;
cin>>T;
while(T--)
{
scanf("%d%d",&N,&L);
for( int i=0; i<=maxn; i++ ) LIS[i]=INF;
memset ( dp,0,sizeof dp );
for( int i=0; i<N; i++ )
{
scanf( "%d",&a[i] );
b[i]=-a[i];
}
for( int i=N-1; i>=0; i-- )
{
int pos=lower_bound( LIS, LIS+N, b[i] )-LIS;
if( b[i]<LIS[pos] )LIS[pos]=b[i];
dp[i]=pos+1;
}
for( int i=0; i<=maxn; i++ ) LIS[i]=INF;
int ans=0;
for( int i=L; i<N; i++ )
{
int pos=lower_bound( LIS, LIS+N, a[i] )-LIS;
ans=max( ans, dp[i]+pos );
pos=lower_bound( LIS, LIS+N, a[i-L] )-LIS;
LIS[pos]=a[i-L];
if( i==N-1 ) //删去最后的L元素
{
int pos=lower_bound( LIS, LIS+N, a[i-L] )-LIS;
ans=max( ans, pos+1 );
}
}
printf( "Case #%d: %d\n",++kase, ans );
}
return 0;
}