🍑 OJ专栏
[NOIP1999 普及组] 导弹拦截
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
一行,若干个整数,中间由空格隔开。
输出格式
两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例 #1
样例输入 #1
389 207 155 300 299 170 158 65
样例输出 #1
6
2
提示
对于前
50
%
50\%
50% 数据(NOIP 原题数据),满足导弹的个数不超过
1
0
4
10^4
104 个。该部分数据总分共
100
100
100 分。可使用
O
(
n
2
)
\mathcal O(n^2)
O(n2) 做法通过。
对于后
50
%
50\%
50% 的数据,满足导弹的个数不超过
1
0
5
10^5
105 个。该部分数据总分也为
100
100
100 分。请使用
O
(
n
log
n
)
\mathcal O(n\log n)
O(nlogn) 做法通过。
对于全部数据,满足导弹的高度为正整数,且不超过 5 × 1 0 4 5\times 10^4 5×104。
此外本题开启 spj,每点两问,按问给分。
upd 2022.8.24 \text{upd 2022.8.24} upd 2022.8.24:新增加一组 Hack 数据。
👨🏫 LIS百科
🍑 AC O(n logn)
import java.util.Scanner;
public class Main
{
static int N = (int) 1e5 + 10;
static int[] a = new int[N];
static int[] f = new int[N];// 最长不上升子序列
static int[] s = new int[N];
// 返回第一个不大于 (<=) x 的数的下标 (lowerBound)
static int find(int l, int r, int x)
{
while (l < r)
{
int m = l + r >> 1;
if (x <= f[m])// <= 不包含
l = m + 1;
else
r = m;
}
return r;
}
// 找到第一个 不小于(>=) x 的数的下标
static int lowerBound(int l, int r, int x)
{
while (l < r)
{
int mid = l + r >> 1;
if (x > s[mid])
l = mid + 1;
else
r = mid;
}
return l;
}
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
String[] ss = sc.nextLine().split(" ");
int n = ss.length;
for (int i = 0; i < n; i++)
a[i + 1] = Integer.parseInt(ss[i]);
int len = 1;
f[len] = a[1];
for (int i = 2; i <= n; i++)
{
if (a[i] <= f[len])// <= 即不上升
{
f[++len] = a[i];// 把此字符加到序列末尾
} else
{
int idx = find(1, len, a[i]);
// 替换掉当前序列中 <= 它的第一个字符(注意:这里是不上升非严格下降
f[idx] = a[i];)
}
}
System.out.println(len);
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (a[i] > s[cnt])
s[++cnt] = a[i];
else
{
int idx = lowerBound(1, cnt + 1, a[i]);
s[idx] = a[i];
}
}
System.out.println(cnt);
}
}
👨🏫 不错的题解