最少拦截系统

最少拦截系统

题目链接
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.

Input

输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)

Output

对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.

Input

8 389 207 155 300 299 170 158 65

Output

2

思路

贪心写法

每一个导弹系统能拦截的导弹序列, 都是一个严格下降子序列。

从头遍历, 刚开始 389 自己算一套导弹系统
对于 207, 有两个选择:

  1. 选择加入到之前结尾大于等于207的最小的序列中
  2. 如果现有序列结尾数都小于207, 则创建新序列

为什么是最小的大于等于207的序列, 而不是任意一个都行呢?
假设存在两个序列结尾为 a和b
且 a > b >= 207
207既可以接在 a 后面, 也可以接在 b 后面
若207接在a后面, 那么b序列只能再占一个系统
若207接在b后面, 因为b又可以接在a后面, 那么就只需要一个系统
相比之下, 207接在最小的大于等于207的序列后是最优解

序列1 389 接 207 接 155
遍历到300时, 找不到符合要求的序列, 自己新开一个
序列2 300 接 299 接 170 接 158
对于65, 它可以接在 序列1, 也可以接在序列2, 我们就贪心地让它接在距离它最近的一个–序列1

最后序列为

  • 389–207–155–65
  • 300–299-170–158

这题的贪心思想跟最长上升子序列的优化解法很像, 实现方法也相同:
定义一个数组 g[N], 存每个序列的最小结尾
遇到一个新导弹时, 遍历整个 g[N], 找到最小的且大于等于该导弹高度的, 更新 g[i]
找不到时就在数组尾新加一位。

最终答案就是整个数组的长度。

顺便一提, 整个g数组是单调递增的, 因为每新加的一个数都是在之前找不到比自己大的才会新加。
故遍历整个g[N] 时可以用二分来优化。

cnt 是最终系统数量
k 是用来找到g数组中最后一个大于等于 a[i] 的位置

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e3 +10;
int a[N], g[N];
int main()
{
	int n;
	while(cin >> n && n)
	{
		int cnt = 0;
		for(int i = 0; i < n;i ++)
		{
			cin >> a[i];
			int k = 0;
			while(k < cnt && g[k] < a[i]) k++;
			g[k] = a[i];
			if(k >= cnt) cnt++;
			// 也可以 cnt = max(cnt, k + 1);
		}
		cout << cnt << endl;
	}
	return 0;
}

DP写法

上面分析给出了一个结论:
g数组是一个单调递增数列,也就是一个严格上升子序列

换角度一想, 我们求的就是这个严格上升子序列的最长长度。
诶那不跟最长上升子序列是一样的吗?

事实确实如此, 一组数中用最少的下降序列全覆盖的个数跟这组数中的最长上升子序列长度相同。

那就很简单了:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e3 +10;
int a[N], f[N];
int main()
{
	int n;
	while(cin >> n && n)
	{
		int res = 0;
		for(int i = 0; i < n;i ++)
		{
			cin >> a[i];
			f[i] = 1;
			for(int j = 0; j < i; j++)
			{// 会相等嘛?并不会, g数组相等的会在一个位置
				if(a[i] > a[j])
					f[i] = max(f[i], f[j] + 1);
				res = max(f[i],res);
			}
		}
		cout << res << endl;
	}
	return 0;
}

贪心写法是 15ms, DP写法是 30ms, 毕竟贪心本身就是求最长上升子序列的优化解法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值