1010. 拦截导弹,贪心(附贪心证明),dp

1010. 拦截导弹 - AcWing题库

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。

但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。

某天,雷达捕捉到敌国的导弹来袭。

由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

共一行,输入导弹依次飞来的高度。

输出格式

第一行包含一个整数,表示最多能拦截的导弹数。

第二行包含一个整数,表示要拦截所有导弹最少要配备的系统数。

数据范围

雷达给出的高度数据是不大于 3000030000 的正整数,导弹数不超过 10001000。

输入样例:
389 207 155 300 299 170 158 65
输出样例:
6
2

 解析:


贪心猜想:
从前往后扫描每个数,对于每个数:
情况1:如果现存的下降子序列的结尾的数都小于当前的数,则创建新的子序列
情况2:将当前数放到子序列结尾数最小的大于等于它的子序列后面

贪心猜想的证明:
令 A = 贪心的结果,B = 最优解;
则只需证明 A<=B 且 B<=A;(常用贪心证明方式)

B<=A:
这显然成立,因为 B 为最优解
A<=B:
使用调整法:
最优情况下放置的位置非当前 X 放置的位置,则最有情况下放置的位置的结尾数一定小于当前 X 放置的位置,则我们根据上述贪心的方法一定可以将 X 放置到最优情况的位置,且不增加下降子序列的个数
所以:A<=B
综上所述,此贪心方法一定可以得到最优解

我们发现这个方法是最长上升子序列问题 的贪心解法
896. 最长上升子序列 II - AcWing题库

 这道题的另一种就是直接使用Dilworth 定理

 dp1,ACM暑期培训-CSDN博客

这里我们实际上是将这个定理给证明了;

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1e3 + 5;
int n;
int q[N], f[N],g[N];

int main() {
	while (cin >> q[n])n++;
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		f[i] = 1;
		for (int j = 1; j < i; j++) {
			if (q[i] <= q[j])
				f[i] = max(f[i], f[j] + 1);
		}
		ans = max(ans, f[i]);
	}
	cout<<ans<<endl;
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		int k = 0;
		while (k < cnt && g[k] < q[i])k++;
		g[k] = q[i];
		if (k >= cnt)cnt++;
	}
	cout << cnt << endl;
	return 0;
}

posted @ 2023-11-11 21:48  Landnig_on_Mars  阅读(186)  评论(0编辑  收藏  举报  来源