DP - 最長上升子序列 - Defense Lines - UVA - 1471
題意:
T組測試數據。給定一個長度爲n的序列,要求刪除其中的一部分連續的序列,使得剩下的序列當中的連續上升子序列盡量的長。要求刪除過後,連續上升子序列最大的長度。
Sample Input:
2
9
5 3 4 9 2 8 6 7 1
7
1 2 3 10 4 5 6
Output:
4
6
數據範圍:
T<=25,n<=200000。
Time limit:3000ms
題解:
本題特殊在連續:刪除一部分連續序列,求剩下部分連續上升子序列的長度。
最暴力的做法是枚舉以a[i]結尾的最長連續上升子序列和以a[j]開頭的最長連續上升子序列,i<j若a[i]<a[j]則可以將區間(i,j)內的元素刪除,最大長度就是兩部分序列長度的和。枚舉的部分是O(n2)的,計算兩部分長度是O(n)的,總的時間復雜度是O(n3)。
計算兩部分連續上升子序列的長度這一步驟可以利用前綴和預處理,這樣能優化到O(n2)。
用l[i]數組記錄以元素a[i]結尾的連續最長上升子序列的長度,r[i]記錄以a[i]開頭的連續最長上升子序列的長度。
這樣,我們可以用單調隊列q[i]優化,維護長度爲i的連續最長上升子序列的末尾元素。
對於每個元素a[i],在q中查找第一個大於等於a[i]的位置len,這個位置以a[i]結尾的序列的長度。
r[i]是以a[i]開頭的連續最長上升子序列的長度,用ans維護len+r[i]−1的最大值。最後將a[i]插入到隊列的l[i]的位置。
代碼:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+10;
int T,n,a[N],l[N],r[N],q[N];
int main()
{
cin>>T;
while(T--)
{
memset(l,0,sizeof l);
memset(r,0,sizeof r);
memset(q,0x3f,sizeof q);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
l[i]=1;
if(a[i]>a[i-1])
l[i]+=l[i-1];
}
for(int i=n;i;i--)
{
r[i]=1;
if(a[i]<a[i+1])
r[i]+=r[i+1];
}
int ans=0;
for(int i=1;i<=n;i++)
{
int len=lower_bound(q+1,q+1+n,a[i])-q;
ans=max(ans,r[i]+len-1);
q[l[i]]=min(q[l[i]],a[i]);
}
cout<<ans<<endl;
}
return 0;
}