(详解!)简单动态规划实例——导弹拦截

问题描述
  某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
  输入导弹依次飞来的高度(雷达给出的高度数据是不大于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]);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Demonslzh6

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

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

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

打赏作者

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

抵扣说明:

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

余额充值