[NOIP1999 普及组] 导弹拦截(5-5)

318 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷

题目描述

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

输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入共一行,若干个整数,中间由空格隔开。

输出共两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

intput

389 207 155 300 299 170 158 65

output

6
2

提示

对于前 50%50\% 数据(NOIP 原题数据),满足导弹的个数不超过 10410^4 个。该部分数据总分共 100100 分。可使用O(n2)\mathcal O(n^2) 做法通过。
对于后 50%50\% 的数据,满足导弹的个数不超过 10510^5 个。该部分数据总分也为 100100 分。请使用 O(nlogn)\mathcal O(n\log n) 做法通过。

对于全部数据,满足导弹的高度为正整数,且不超过 5×1045\times 10^4

题目分析

第一问即求最长不上升子序列。

第二问中,需要求个数最少的最长不上升子序列,满足包含所有元素。

我们假设目前已经有 nn 个序列,当前值为 ii,可以用贪心的方法证明,ii 应加到末尾元素大于 ii 且为满足此条件的末尾元素最小的序列中。

可以看出此方法为求最长上升子序列的 O(nlogn)O(nlogn) 法,即第二问转化为求最长上升子序列的元素个数。

Accept代码(DP+贪心)

#include <bits/stdc++.h>

using namespace std;

const int N = 1010;
int n, a[N];
int tt, f[N];

int main()
{
    int x;
    while (cin >> x) a[++ n] = x;
    
    f[0] = 3e5;
    for (int i = 1; i <= n; i ++)
    {
        if (a[i] <= f[tt]) f[++ tt] = a[i];
        else
        {
            int l = 1, r = tt;
            while (l < r)
            {
                int mid = l + r >> 1;
                if (f[mid] < a[i]) r = mid;
                else l = mid + 1;
            }
            f[r] = a[i];
        }
    }
    cout << tt << "\n";
    
    tt = 0, f[0] = 0;
    for (int i = 1; i <= n; i ++)
    {
        if (a[i] > f[tt]) f[++ tt] = a[i];
        else
        {
            int l = 1, r = tt;
            while (l < r)
            {
                int mid = l + r >> 1;
                if (f[mid] >= a[i]) r = mid;
                else l = mid + 1;
            }
            f[r] = a[i];
        }
    }
    cout << tt;
    return 0;
}