【动态规划】导弹拦截

291 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

[NOIP1999 普及组] 导弹拦截

题目描述

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

输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

一行,若干个整数,中间由空格隔开。

输出格式

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

样例 #1

样例输入 #1

389 207 155 300 299 170 158 65

样例输出 #1

6
2

提示

对于前 50%50\% 数据(NOIP 原题数据),满足导弹的个数不超过 20002000 个。该部分数据总分共 100100 分。可使用O(n2)\mathcal O(n^2) 做法通过。
对于后 50%50\% 的数据,满足导弹的个数不超过 10510^5 个。该部分数据总分也为 100100 分。请使用 O(nlogn)\mathcal O(n\log n) 做法通过。

对于全部数据,满足导弹的高度为正整数,且不超过 5×1045\times 10^4

此外本题开启 spj,每点两问,按问给分。

解题思路

将拦截的导弹的高度提出来成为原高度序列的一个子序列,根据题意这个子序列中的元素是单调不增的(即后一项总是不大于前一项),我们称为单调不升子序列。本问所求能拦截到的最多的导弹,即求最长的单调不升子序列。 从左到右依次枚举每个导弹。假设现在有若干个导弹拦截系统可以拦截它,那么我们肯定选择这些系统当中位置最低的那一个。如果不存在任何一个导弹拦截系统可以拦截它,那我们只能新加一个系统了。

#include<bits/stdc++.h>
using namespace std;
int dp[1000005],a[100005],maxn,t=1;
int main(){
  while(cin>>a[t]) t++;//while 读入
  for(int i=t-1;i>=1;i--){//最长下降子序列
      dp[i]=1;//初始化为1
      for(int j=i+1;j<t;j++){//要反着来
          if(a[j]<=a[i]) dp[i]=max(dp[i],dp[j]+1);//dp
      }
      maxn=max(maxn,dp[i]);//取最大
  }
  cout<<maxn<<endl;
  maxn=0;//记得要弄成0!
  for(int i=1;i<t;i++){//最长上升子序列
      dp[i]=1;//初始化为1
      for(int j=1;j<i;j++){
          if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1);//dp
      }
      maxn=max(maxn,dp[i]);//取最大
  }
  cout<<maxn;
}