【題目鏈接】
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;
}