问题描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
一行,为导弹依次飞来的高度
输出格式
两行,分别是最多能拦截的导弹数与要拦截所有导弹最少要配备的系统数
样例输入
389 207 155 300 299 170 158 65
样例输出
6
2
动态规划
这题是典型的动态规划问题,对于动态规划,我们需要开辟一个数组来存放每一步的最优解,用来找到整体的最优解。
一个简单的例子如下:
求解右式:1+1+1=?
毫无疑问结果是3,对于该式我们的解法其实就是数有多少个1。
接下来,我们在左式后再加上一个"+1",结果又是多少呢?答案很明显是4。我们是如何得出这个4的呢?实际的计算过程就是将上一步得出的结果直接加上了1。每一步的结果我们实际上都有作一个“保存”,下一步便可依此找寻最优解。这便是动态规划的基本思想。
再来看这道题。需要输出的结果有两个,一个是一次最多能拦截的导弹数(最长下降子序列),和拦截所有导弹需要的系统个数(最长上升子序列)。这两个结果在实际求解中是一组互逆的运算,针对其中一个问题求出了结果,另一个也就迎刃而解了。
那么现在主要分析能拦截到的最大导弹数量。面对每一个导弹飞来的高度,如果当前高度比前一个高度低,那么可以拦截的导弹数+1,否则不作变换。对于每一步的结果dp[]我们初始化为1,因为每个导弹都可以对自身高度的导弹进行拦截。
dp : 1 1 1 1 1 1 1 1
整个的过程如下,其中Height为每个导弹飞来的高度,dp为每个导弹飞来后得出的当前最优解。
Height:389 207 155 300 299 170 158 65
dp : 1 2 3 2 3 4 5 6
所以当当前导弹高度小于之前的导弹高度(即定义循环变量i,遍历所有导弹高度,循环变量j,遍历已经飞过导弹高度。是否满足Height[i]<=Height[j] ),且可以拦截的导弹数量也小于之前导弹的数量时(dp[i]<=dp[j]),满足条件,可以拦截。那么对应的dp[i]+1。
由此我们可以推出它的状态转移方程:
dp[i]=max(dp[i],dp[j]+1)
那么不难写出核心代码如下:
for(int i=0;i<n;i++) {//n为输入的导弹数量
for(int j=0;j<i;j++) {
if(Height[i]<=Height[j] )
dp[i]=Math.max(dp[i], dp[j]+1);
}
}
最终我们对求得的结果dp进行排序,取最大值即为可以拦截的最大导弹数。
对于最大上升子序列(拦截系统个数),我们只需要再开辟一个存放结果数组dp2[],初始化为1,当不满足Height[i]<=Height[j] 时,将dp2[]中对应的结果+1。即 dp2[i]=Math.max(dp2[i], dp2[j]+1);
完整代码如下↓ ↓ ↓
import java.util.*;
public class 导弹拦截 {
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
String str = sc.nextLine();
String[] arr = str.split(" ");
int n=arr.length;
int[] Height = new int[n];
int[] dp=new int[n];
int[] dp2=new int[n];
for(int i=0;i<n;i++)
{
Height[i]=Integer.valueOf(arr[i]);
dp[i]=1;
dp2[i]=1;
}
int flag=0;
for(int i=0;i<n;i++) {
for(int j=0;j<i;j++) {
if(Height[i]<=Height[j] )
dp[i]=Math.max(dp[i], dp[j]+1);
else
dp2[i]=Math.max(dp2[i], dp2[j]+1);
}
}
Arrays.sort(dp);
Arrays.sort(dp2);
System.out.println(dp[dp.length-1]);
System.out.println(dp2[dp2.length-1]);
}
}