Noip 1999 普及组 导弹拦截 dp+二分查找

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

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

输入输出格式

输入格式:

11 行,若干个整数(个数 \le 100000≤100000 )

输出格式:

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

输入输出样例

输入样例#1: 复制

389 207 155 300 299 170 158 65

输出样例#1: 复制

6
2

说明

为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分

每点两问,按问给分

传送门(洛谷)

死人回归了……!!

几乎一年没接触OI的感觉真TM爽。。。

罢了既然决定回来做点题那就讲题解把QAQ。。。

就当水了一篇文章了……反正都是水题大家都没兴趣的罢QAQ

由于代码现在写得生疏了,所以随便找了一道没做过的普及题做……

从某种角度上说,其实我是做过的,但是很遗憾第二问的定理没记住。

第一问显然就是一个最长不上升子序列,可以说是dp,

只不过优化成二分样子而已。

而第二问我的思考历程完全没有经过“最长上升子序列”,这是最后我看别人的时候发现的。

所以题解算法我写的是dp+二分。。。

第二问:起初我考虑可能有个贪心的思想,就是每一段波折就新开一个系统。

这个思想是来源于,当一段连续上升的序列出现时,他们每一个至少消耗一个系统。

把多个系统看作一条条链,这个链是跳跃着的,于是我便考虑是不是连续的链可以用最少个数。

但是这个想法过于初级,问题来得很快,随便一个基本数据就可以打垮:

导弹高度分别为:(从左到右)8 6 7 4 5 2 3 1

只用2个系统,但是贪心的话会用到……好多

也就是说“短促的波浪会干扰贪心”,特别是递减的短促波浪。

这是我们可以考虑一下一小部分的问题:比如8 6 7

2套系统,该如何安排呢?是8->6,7还是8->7,6呢?显而易见8->7会更好,这样子为后面余地更多,

而且,不管怎么安排,这两条链的结尾数字永远都是6和7

这是整个思想的来源——我们可以建立一个数组b[i],记录链的条数len,

b[i]表示第i条链的末尾数字。

当一个新的导弹进入,我们记它为x,那么x该如何处理呢?能直接接在某条链上么?

分情况讨论:

1.x比b中最大的bmax还要大,那么肯定要新开一条链,结尾是x

2.x比某一个b[k]小,并且除了>=b[k]的,都比x小

这种情况下,也就是b[k]最接近x并且b[k]>x,那么将b[k]改成x

因为当x加入时,一定有一条链的末尾是x,

2情形下这条链一定是从之前选择的——从贪心的角度,选择最接近x的并且比x大的某个b[k]。

算法清晰了,主要就是实现的问题。

第一直觉是维护b[k]有序,详细点,就是维护b[k]上升

那么情形1是O(1)处理(直接接在最后端)

情形2,由于是有序的,所以可以二分。

有没有发现什么?没错!这就是最长上升子序列的O(nlogn)算法……

具体定理好像叫做dilworh?但是两者间的联系值得思考……

刚回来手生得很啊。。代码打了1h,还得调试。。

凌晨熬夜回看了一些内容。。。大概复习复习

由于看老师不爽,暑假培训没去,所以所有东西都只能自己弄了。。

很累,但是效果拔群啊2333

没错!重新回来了!踏实踏实~

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000;
int n,a[MAXN+5],b[MAXN+5];
void modify1(int x,int len){
	if (x>b[1]){b[1]=x;return;}
	int l=1,r=len,m;
	while (l<r){
		m=(l+r)>>1;
		if (b[m]>=x && b[m+1]<x){b[m+1]=x;return;}
		if (b[m]<x) r=m;
		if (b[m+1]>=x) l=m+1;
	}
}
void modify2(int x,int len){
	if (x<b[1]){b[1]=x;return;}
	int l=1,r=len,m;
	while (l<r){
		m=(l+r)>>1;
		if (b[m]<x && b[m+1]>x){b[m+1]=x;return;}
		if (b[m+1]<=x) l=m+1;
		if (b[m]>=x) r=m;
	}
}
int main(){
	n=1;
	while (~scanf("%d",&a[n])) n++;n--;
	int ans=1;b[1]=a[1];
	for (int i=2;i<=n;i++)
		if (a[i]<=b[ans]) b[++ans]=a[i];
		 else modify1(a[i],ans);
	printf("%d\n",ans);

	ans=1;b[1]=a[1];
	for (int i=2;i<=n;i++){
//		cout<<i<<endl;
//			for (int k=1;k<=ans;k++) cout<<b[k]<<' ';cout<<endl;
		
		if (a[i]>b[ans]) b[++ans]=a[i];//,cout<<b[ans]<<endl;
		 else 
		if (a[i]==b[ans]) continue;
		 else modify2(a[i],ans);
//		if (i==4){
//			for (int k=1;k<=ans;k++) cout<<b[ans]<<' ';cout<<endl;
//		}		
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ThinFatty/article/details/81605318