P1020 [NOIP1999 普及组] 导弹拦截


题目

本博客给出本题截图
在这里插入图片描述
首先需要 说明:这个题目在 AcWing 中也有一模一样的题目,但是两个题目的数据范围不同:

AcWingAcWing 1010. 拦截导弹
洛谷P1020 [NOIP1999 普及组] 导弹拦截

洛谷 中的数据范围更大,本题提供两个代码,第一个代码在 AcWing 中可以 AC,但是在洛谷中有一半的数据会 TLE,第二个代码则在洛谷中也可以过掉

AC代码1

代码解释
f数组表示的是在前 i 个导弹中的最长不上升子序列的长度,最后对所有的 f[i] 取一个最大值即可
g数组存储的是每一个防伪系统中的结尾导弹高度,对于每一个 h 数组中的元素,我们都会有两种选择,开一个新的防卫系统,或者是接到某一个系统的结尾

代码

#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 100010;

int f[N];
int g[N];
int h[N];
int n;

int main()
{
	while (scanf("%d", &h[n]) != EOF) n ++;
	
	int res = 0;
	for (int i = 0; i < n; i ++ ) 
	{
		f[i] = 1;
		for (int j = 0; j < i; j ++ ) 
			if (h[j] >= h[i])	
				f[i] = max(f[i], f[j] + 1);
		
		res = max(res, f[i]);
	}
	
	printf("%d\n", res);
	
	int cnt = 0;
	for (int i = 0; i < n; i ++ )
	{
		int k = 0;
		while (k < cnt && g[k] < h[i]) k ++;
		g[k] = h[i];
		if (k >= cnt) cnt ++;
	}
	
	printf("%d\n", cnt);
	
	return 0;
}

upper_bound 和 lower_bound

介绍第二个代码之前先来介绍一下lower_boundupper_bound,使用案例见博客:STL—algorithm(2)

若数组升序排列:
lower_bound(begin, end, a) 返回数组[begin, end)之间第一个大于或等于a的地址,找不到返回end
upper_bound(begin, end, a) 返回数组[begin, end)之间第一个大于a的地址,找不到返回end

若数组降序排列:
lower_bound(begin, end, a, greater<int>()) 返回数组[begin, end)之间第一个小于或等于a的地址,找不到返回end
upper_bound(begin, end, a, greater<int>()) 返回数组[begin, end)之间第一个小于a的地址,找不到返回end

注意:在使用这两个函数之前一定需要保证数组是有序的

AC代码2

代码解释
本题中数组的作用在代码部分已经进行了标注,注意根据对于数组的定义,两个数组肯定是有序的

这个题说白了就是求一个最长不上升序列的长度 (不是子序列) 和一个最长上升序列的长度 (不是子序列)

求最长上升序列的长度:
求这个序列的长度实际上就是在求这套系统最多能拦截多少导弹:从前往后依次去看导弹的高度,只会有两种情况:
1.这个导弹的高度要小于等于f数组末尾的高度,那么我们就把这个导弹高度插入到我们的f数组的末尾
2.这个导弹的高度要大于f数组末尾的高度,那么我们就在f数组中找到第一个小于它的数,这里就需要用到upper_bound,然后用这个导弹的高度,去替换掉我们找到的这个数

为什么要替换掉呢?我们把上述中的导弹高度称为a,要被替换掉的导弹的高度称为b,那么如果b在末尾,那么由于b < a,所以b后面能接的导弹数不如a的多,如果b在内部,那么b在有生之年内绝对不会再被用到了,所以把他替换掉也是无所谓的事情,注意在进行上述的一切操作中,我们的f数组内部都是有序的,读者可以想一下是为什么.

再次强调:我们求的是最长上升序列的长度,不是子序列!!!

最长上升序列的长度:求法和思路同上述,这里就不再进行过多的赘述

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int f[N];  //f数组存储的是最长不上升序列(注意不是子序列) 
int g[N];  //g数组存储的是最长上升序列(注意不是子序列)
int h[N];
int n;

int main() 
{
	while (cin >> h[++ n]);
	n --;
	
	int res = 1, cnt = 1;
	f[1] = g[1] = h[1];
	for (int i = 2; i <= n; i ++ ) 
	{
		if (h[i] <= f[res]) f[++ res] = h[i];
		else
		{
			int k = upper_bound(f + 1, f + res + 1, h[i], greater<int>()) - f;
			f[k] = h[i];
		}
		
		if (h[i] > g[cnt]) g[++ cnt] = h[i];
		else
		{
			int k = lower_bound(g + 1, g + cnt + 1, h[i]) - g;
			g[k] = h[i];
		}
	}
	
	printf("%d\n%d\n", res, cnt);
	
	return 0;
}

总结

才疏学浅,第一次真正运用到 lower_boundupper_bound,真香.

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐