题解 P1020 【导弹拦截】

题目

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是 \le 50000≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

数据范围:

子问题1:n <= 1000

子问题2:n <= 100000

思路:序列DP + 线段树优化DP

首先第一个问题,其实就是求最长不上升序列。

我们设计 dp[i] 为从 1 到 i 且其最长不上升序列以 i 为结尾的序列长度。那么我们可以从比当颗导弹高或相等的导弹中更新答案。即为:

dp[i] = max{dp[v]} + 1 (a[v] >= a[i] && v <= i)

这里时间复杂度为O(n^2)。

对于第二个问题,我们可以想到,假如我们先用一个拦截设施把所有能打下来的导弹都打下来,剩下的拦截设施重复这个动作,一直到所有导弹被拦截。假设我们得到的是最小划分(题目要求)的k组导弹。那么对于第 i 组导弹中任取,在第 i + 1 必定有一个导弹要比这颗导弹高,如果没有,那么我们完全可以把 i + 1 组合并到第 i 组。所以我们找到一个最长的一个上升子序列,肯定序列中的每个导弹对应组导弹,如果多出的话肯定是多余的,如果少的话,不符合上述的分组。所以我们在找最小划分时,只需要找到一个最长上升子序列长度即可。

我们重新设计 dp[i] 为从 1 到 i 且其最长上升子序列以 i 为结尾的序列长度。同上,我们可以得到:

dp[i] = max{dp[v]} + 1 (a[v] < a[i] && v <= i)

时间复杂度也为O(n^2)

对于优化:(两问通用)

我们可以发现有一维是用来求 dp[] 中满足条件的最大值。我们可以考虑用线段树来优化。我们以 a[i] (就是高度)为坐标,在线段树中维护 f[i] 的最大值。我们在进行有规律的遍历时,每当更新一次 f[i] 我们就将他在线段树中更新,在寻找最大值时,我们按照大小,在相应区间(比如求最长上升子序列中,我们要从高度小于 a[i] 的 f[v] 中转移,所以我们询问 [1, a[i] - 1] 区间的信息)询问,并进行转移即可。

时间复杂度为O(nlogn)

code :

P.S. 代码中注释掉的是没优化时的部分代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<stack>
using namespace std;
#define go(i, j, n, k) for(int i = j; i <= n; i += k)
#define fo(i, j, n, k) for(int i = j; i >= n; i -= k)
#define rep(i, x) for(int i = h[x]; i; i = e[i].nxt)
#define mn 100010
#define inf 1 << 30
#define ll long long
#define root 1, 50000, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define bson l, r, rt
inline int read(){
    int x = 0, f = 1; char ch = getchar();
    while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
int minn[mn << 2], maxx[mn << 2];
inline void update(int rt) {
    minn[rt] = min(minn[rt << 1], minn[rt << 1 | 1]);
    maxx[rt] = max(maxx[rt << 1], maxx[rt << 1 | 1]);
}
inline void build(int l, int r, int rt) {
    if(l == r) { minn[rt] = maxx[rt] = 0; return; }
    int m = (l + r) >> 1; build(lson), build(rson), update(rt);
}
inline void modify(int l, int r, int rt, int now, int v) {
    if(l == r) {
        minn[rt] = v; maxx[rt] = v;
        return;
    }
    int m = (l + r) >> 1;
    if(now <= m) modify(lson, now, v);
    else modify(rson, now, v);
    update(rt);
}
inline int query(int l, int r, int rt, int nowl, int nowr) {
    if(nowl <= l && r <= nowr) return maxx[rt];
    int m = (l + r) >> 1;
    if(nowl <= m) {
        if(m < nowr) return max(query(lson, nowl, nowr), query(rson, nowl, nowr));
        else return query(lson, nowl, nowr);
    } else return query(rson, nowl, nowr);
}
int f[mn], a[mn], n, ans1, ans2;
int main(){
    while(cin >> a[++n]) ;
    n--;
    go(i, 1, n, 1) f[i] = 0;
    build(root);
    go(i, 1, n, 1) {
        int maxx = 0, temp = 0;
//      go(j, 1, i - 1, 1) {
//          if(maxx < f[j] && a[i] <= a[j]) {
//              temp = j;
//              maxx = f[j];
//          }
//      }
        maxx = query(root, a[i], 50000);
        f[i] = maxx + 1;
        modify(root, a[i], f[i]);
        ans1 = max(f[i], ans1);
    } 
    cout << ans1 << "\n";
    go(i, 1, n, 1) f[i] = 0; 
    build(root);
    go(i, 1, n, 1) {
        int maxx = 0, temp = 0;
//      go(j, 1, i - 1, 1) {
//          if(maxx < f[j] && a[i] > a[j]) {
//              temp = j;
//              maxx = f[j];
//          }
//      }
        maxx = query(root, 1, a[i] - 1);
        f[i] = maxx + 1;
        modify(root, a[i], f[i]);
        ans2 = max(f[i], ans2);
    }
    cout << ans2 << "\n";
    return 0;
}
上一篇:洛古 P1020 导弹拦截 N^2


下一篇:shell编程题(二十三)