首先第一个问题,其实就是求最长不上升序列
我们设计 d p i {dp}_{i} dpi 为从 1 到 i i i 且其最长不上升序列以 i i i 为结尾的序列长度。那么我们可以从比当颗导弹高或相等的导弹中更新答案。即为:
dp[i] = max{dp[v]} + 1 (a[v] >= a[i] && v <= i)
对于第二个问题,我们可以想到,假如我们先用一个拦截设施把所有能打下来的导弹都打下来,剩下的拦截设施重复这个动作,一直到所有导弹被拦截。假设我们得到的是最小划分(题目要求)的 k k k 组导弹。那么对于第 i i i 组导弹中任取,在第 i + 1 i+1 i+1 必定有一个导弹要比这颗导弹高,如果没有,那么我们完全可以把 i + 1 i+1 i+1 组合并到第 i i i 组。
所以我们找到一个最长的一个上升子序列,肯定序列中的每个导弹对应组导弹,如果多出的话肯定是多余的,如果少的话,不符合上述的分组。所以我们在找最小划分时,只需要找到一个最长上升子序列长度即可。
我们重新设计 d p i {dp}_{i} dpi 为从 1 到 i i i 且其最长上升子序列以 i i i 为结尾的序列长度。同上,我们可以得到:
dp[i] = max{dp[v]} + 1 (a[v] < a[i] && v <= i)
对于优化 : (两问通用)
我们可以发现有一维是用来求 d p 1.. n {dp}_{1..n} dp1..n 中满足条件的最大值。我们可以考虑用线段树来优化。我们以 a i a_{i} ai (就是高度)为坐标,在线段树中维护 f i f_{i} fi 的最大值。我们在进行有规律的遍历时,每当更新一次 f i f_{i} fi 我们就将他在线段树中更新,在寻找最大值时,我们按照大小,在相应区间(比如求最长上升子序列中,我们要从高度小于 a i a_{i} ai 的 f v f_{v} fv 中转移,所以我们询问 ( 1 , a [ i ] − 1 ) (1,a[i]-1) (1,a[i]−1) 区间的信息)询问,并进行转移即可。
#include
#include
using namespace std;
#define root 1,1,maxn
#define ls p<<1,l,m
#define rs p<<1|1,m+1,r
const int maxn=(int)5e4+10;
int Min[maxn<<2], Max[maxn<<2];
inline void update(int p) {
Min[p]= min(Min[p<<1], Min[p<<1|1]);
Max[p]= max(Max[p<<1], Max[p<<1|1]);
return;
}
inline void build(int p,int l, int r) {
if(l==r){ Min[p]=Max[p]=0;return; }
int m= (l+r)>>1;
build(ls); build(rs); update(p);
return;
}
inline void modify(int p, int l, int r, int now, int v) {
if(l==r){ Min[p]=Max[p]=v;return; }
int m= (l+r)>>1;
if(now <= m) modify(ls, now, v);
else modify(rs, now, v);
update(p); return;
}
inline int query(int p, int l, int r, int li, int ri) {
if(li<=l && r<=ri) return Max[p];
int m= (l+r)>>1;
if(li <= m) {
if(m < ri) return max( query(ls, li, ri), query(rs, li, ri) );
else return query(ls, li, ri);
}
else return query(rs, li, ri);
}
int f[maxn]={0}, a[maxn]={0};
int n=0, ans1=0, ans2=0, i=0;
int max_i=0;
int main(){
while(scanf("%d",&a[++n])!=EOF); n--;
for(i=1;i<=n;i++) f[i]=0;
build(root);
for(i=1;i<=n;i++) {
max_i=0;
max_i= query(root, a[i], maxn);
f[i]= max_i+1;
modify(root, a[i], f[i]);
ans1= max(f[i], ans1);
}
printf("%d\n",ans1);
for(i=1;i<=n;i++) f[i]=0;
build(root);
for(i=1;i<=n;i++) {
max_i=0;
max_i= query(root, 1, a[i]-1);
f[i]= max_i+1;
modify(root, a[i], f[i]);
ans2= max(f[i], ans2);
}
printf("%d\n",ans2);
return 0;
}