A1929 怪盗基德的滑翔翼(LIS)
求最长上升、下降子序列,取其较大值。
思路一:
设 \(f(x)\) 表示长度为 \(x\) 的序列对应结尾的最优解。
函数具有单调性,可以二分求解。
#include<bits/stdc++.h>
using namespace std;
const int N=109,inf=1e9;
int dp[N],dp2[N],cnt,cnt2;
//dp 求上升子序列,dp2 求下降子序列。
bool cmp(int a,int b){return a>b;}
int main(){
int T,n; scanf("%d",&T);
while(T--){
scanf("%d",&n),cnt=cnt2=0,dp[0]=-inf,dp2[0]=inf;
for(int i=1;i<=n;i++) dp[i]=inf,dp2[i]=-inf;
for(int i=1,a,pos,pos2;i<=n;i++){
scanf("%d",&a);
pos=lower_bound(dp,dp+cnt+1,a)-dp;
pos2=lower_bound(dp2,dp2+cnt2+1,a,cmp)-dp2;
cnt=max(cnt,pos),dp[pos]=min(dp[pos],a);
cnt2=max(cnt2,pos2),dp2[pos2]=max(dp2[pos2],a);
}
printf("%d\n",max(cnt,cnt2));
}
return 0;
}
思路二:
思考暴力转移,是对于每一个位置找前面满足:
- 可以传递
- 长度最大
的一个位置,而这个需求可以使用树状数组解决,
树状数组的下标是高度,值是序列长度,只需要单点修改前缀 max 即可。
最长下降子序列就用 10000 减一下,转换成最长上升子序列。
#include<bits/stdc++.h>
using namespace std;
const int N=10009,lim=10000;
struct tool{
int a[N];
void clear(){
for(int i=1;i<=lim;i++) a[i]=0;
}
int query(int x){
int ans=0;
while(x)
ans=max(ans,a[x]),x-=(x&-x);
return ans;
}
void insert(int x,int k){
while(x<=lim)
a[x]=max(k,a[x]),x+=(x&-x);
}
}A,B;
int main(){
int T,n; scanf("%d",&T);
while(T--){
scanf("%d",&n),A.clear(),B.clear();
for(int i=1,a;i<=n;i++){
scanf("%d",&a);
A.insert(a,A.query(a-1)+1);
a=10000-a;
B.insert(a,B.query(a-1)+1);
}
printf("%d\n",max(A.query(lim),B.query(lim)));
}
return 0;
}

浙公网安备 33010602011771号