洛谷 P1020 导弹拦截 DP + 贪心 + 二分 lower/upper_bound

文章提供了一个Java程序,用于解决导弹拦截问题。该问题要求计算一套导弹拦截系统最多能拦截多少导弹,并确定拦截所有导弹所需的最少系统数量。程序基于最长不上升子序列(LIS)算法,实现了O(nlogn)的时间复杂度,适用于处理不超过10^5个导弹高度的数据。
摘要由CSDN通过智能技术生成

🍑 OJ专栏


🍑 P1020 [NOIP1999 普及组] 导弹拦截

[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);
	}
}


👨‍🏫 不错的题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值