题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是 ≤50000 的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
1 行,若干个整数(个数 ≤100000 )
输出格式:
2 行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输入样例#1:
389 207 155 300 299 170 158 65
输出样例#1:
6
2
思路:线性DP经典题,由于超时问题,最后用记忆化+二分解决问题
源代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int max(int x,int y)//比较函数
{
if(x>y)
return x;
else
return y;
}
int missile[100010],dp[100010];
int main()
{
int n=0,total=0,ans=0;
int i;
int a,b,mid;
while(scanf("%d",&missile[++n])!=EOF);//表示资料源无更多的资料可读取,通常在文本的最后存在此字符表示资料结束,输入时,以ctrl+z为结束
/*求最长不上升子序列*/
dp[0]=99999;//由于导弹最高高度为50000,因此初始化为极大值
for(i=1;i<=n-1;i++)
{
if(dp[total]>=missile[i])//比较最大高度与导弹高度
{
dp[total+1]=missile[i];//记录当前最大高度
total++;//最长子序列加一
}
else//二分查找+记忆化,缩短时间
{
a=0;b=total;//左右区间
while(a<b)
{
mid=(a+b)/2;//取中
if(dp[mid]>=missile[i])//中点高度>=当前导弹高度
a=mid+1;//左区间变为当前导弹高度向后1位
else//中点高度<当前导弹高度
b=mid;//右区间变为当前导弹高度
}
if(a!=0) dp[a]=missile[i];//当左区间不为0时,令左区间起始高度变为当前导弹高度
}
}
cout<<total<<endl;//输出最长子序列长度
/*求最长上升子序列*/
memset(dp,0,sizeof(dp));//将数组全部赋0值
for(i=1;i<=n-1;i++)
{
if(dp[ans]<missile[i])//比较系统最大高度与导弹高度
{
dp[ans+1]=missile[i];//记录最大高度
ans++;//所需系统数+1
}
else//二分查找+记忆化,缩短时间
{
a=0;b=ans;//左右区间
while(a<b)
{
mid=(a+b)/2;//取中
if(dp[mid]>=missile[i])//中点高度>=当前导弹高度
b=mid;//右区间变为当前导弹高度
else//中点高度<当前导弹高度
a=mid+1;//左区间变为当前导弹高度向后1位
}
dp[a]=missile[i];//左区间起始高度变为当前导弹高度
}
}
cout<<ans<<endl;//输出需要多少套系统
return 0;
}