导弹拦截(动规,贪心)

3 篇文章 0 订阅

题目描述

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

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

输入格式

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

输出格式

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

输入

389 207 155 300 299 170 158 65

输出

6
2

众说周知,导弹拦截这个题呢有两种靠谱的写法。一种是动规,时间复杂度为O(N^2)。还一种就是贪心,时间复杂度为O(NlogN)。

动规: O(N^2)
这个题我们看成求最长下降子序列(一个系统最多拦截的导弹数)和最长上升子序列(最多需要几个系统)。
一般这种求最长子序列的,我们可以容易列出转移方程:

dp[i]=max(dp[i],dp[j]+1);

那么上升和下降就是一个a[i]>a[j] 和一个a[i]<=a[j]了。

上代码:

#include "bits/stdc++.h"
using namespace std;
int n,a[100010],dp[100010],dpp[100010],ma,maa;
int main()
{
	while(cin>>a[++n]);
	n--;
	for(int i=1;i<=n;i++) 
	{
		dp[i]=1;dpp[i]=1;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			if(i==j) continue;
			if(a[i]<=a[j])
			dp[i]=max(dp[i],dp[j]+1);
			if(a[i]>a[j])
			dpp[i]=max(dpp[i],dpp[j]+1);
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		ma=max(ma,dp[i]);
		maa=max(maa,dpp[i]);
	}
	cout<<ma<<endl<<maa;
}

ps:由于每个导弹只有一发,所以if(i==j) continue;

贪心: O(NlogN)
我们可以借助lower_bound和upper_bound函数

对于系统拦截的最多导弹数,如果导弹不高于当前拦截高度则拦截,否则从已拦截的导弹中去除过低的导弹,将当前导弹拦截!

if(dp[len1]>=a[i]) dp[++len1]=a[i];
else *upper_bound(dp+1,dp+1+len1,a[i],greater<int>())=a[i];

而对于系统数,则刚好相反

if(dpp[len2]<a[i]) dpp[++len2]=a[i];
else *lower_bound(dpp+1,dpp+1+len2,a[i])=a[i];

于是:

#include "bits/stdc++.h"
using namespace std;
int a[100010],dp[100010],dpp[100010],n;
int main()
{
	while(cin>>a[++n]);
	n--;
	int len1=1,len2=1;
	dp[1]=dpp[1]=a[1];
	for(int i=2;i<=n;i++)
	{
		if(dp[len1]>=a[i]) dp[++len1]=a[i];
		else *upper_bound(dp+1,dp+1+len1,a[i],greater<int>())=a[i];
		if(dpp[len2]<a[i]) dpp[++len2]=a[i];
		else *lower_bound(dpp+1,dpp+1+len2,a[i])=a[i];
	}
	cout<<len1<<endl<<len2;
 } 

ps:因为是下降序列,所以upper_bound要加greater< int >()。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

indolence-阡陌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值