洛谷P1020(导弹拦截)

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

1行,若干个整数(个数≤100000)

输出格式

2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

解题思路

  做了这一题我才发现我好笨啊,不得不说我的dp功底不够。
  #define int num[100000] 数组存储导弹高度序列。

第一问

  第一问就是问一个不上升的最长序列,这个大家都懂,那么怎么求呢,首先构造一个int d1[100000],这个数组里有效数字的长度就是我们要求的不上升序列的长度,注意,我们只需要求长度,但是序列里各个元素是些什么我们不需要知道,于是就有了这一个非常高明的dp方法(我也是看的大佬代码),假设d1末尾的元素是d1[len1-1],新遍历的元素是num[i],如果num[i]小于等于d1[len1-1],那么num[i]就接着d1[len1-1]就好了,因为这是一个不上升序列嘛;但是若num[i]大于d1[len1-1],那么就去d1[0]~d1[len1-1]中找到第一个小于等于num[i]的元素并用num[i]替换掉他。
  为什么可以替换呢?假设找到的那个元素就是d1[len1-1],那么替换掉后,num数组后面出现的元素就更容易尾接上这个序列,如果这个元素在d1的中间某处,那么替换掉后,可以理解成对树的的dp过程中的分叉,作用也是为了让之后的num序列中的数跟容易插入,一直影响到d1的尾端,理解起来很复杂,就可以按照dp的分叉之后覆写理解。

第二问

  第二问先说结果,是构造一个最长上升序列,这个序列的长度就是我们所要求的,我们假设为p,构造的方法和第一问类似。为什么这样呢,首先用第一问的方法可以把num序列分成1,2,3,…,s个单调不上升序列,注意,按照第一问的方法,构造过程中,1序列尽可能长,之后再去构造2序列,2序列也尽可能的长,之后再去构造3序列,一直这样下去。构造中有个小细节我们要注意,如果一个序列中某个位置中填a或b都可以,但是无论填哪个,都对这个序列的长度没有影响,我们就填最小的那个,这样的话对于s的值没有任何影响,大家可以自己去证一下,这样一个规定有什么用呢,请往下看。
  从1,2,3,…,s序列中各取最后一个元素设为t1,t2,t3,…,ts,那么一定有ts>…>t3>t2>t1,假设t1>=t2:如果t2在num数组中出现在t1前面,那么序列1中末尾的元素应该是t2而不是t1,不然就违反了我上面的规定;如果t2在num数组出现在t1后面,那么序列1中t1后面应该接着t2。无论是哪个,都有事实不符,所以t1,t2,t3,…,ts构成了一个上升序列,所以p>=s,同时最长序列中任意两个元素必定不能来自同一个不上升序列,所以p<=s。由夹逼的思想知道,p=s。
  上面是一些数学推导,但是实际编程的时候不用实现,只用仿照第一问的方法构造出单调上升序列就好了。我看到一些大佬们说第二问是Dilworth定理,可惜我愚钝,没有理解。
  Talking is cheap,show you my code.

代码

#include<iostream>
#include <algorithm>
using namespace std;
int n = 0;
int num[100000];
int d1[100000];
int d2[100000];
int main() {
	while (cin >> num[n]) {
		n++;
	}
	int len1 = 0;
	int len2 = 0;
	d1[0] = num[0];
	d2[0] = num[0];
	len1++;
	len2++;
	for (int i = 1;i < n;i++) {
		if (d1[len1 - 1] >= num[i]) {
			d1[len1] = num[i];
			len1++;
		}
		else {
			int p = upper_bound(d1, d1 + len1 - 1, num[i], greater<int>()) - d1;
			d1[p] = num[i];
		}
		if (d2[len2 - 1] < num[i]) {
			d2[len2] = num[i];
			len2++;
		}
		else {
			int p = lower_bound(d2, d2 + len2 - 1, num[i]) - d2;
			d2[p] = num[i];
		}
	}
	cout << len1 << endl << len2 << endl;
	return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值