第6周测验题解-拦截导弹

1. 题目详情

描述

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

输入

输入有两行,
第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),
第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。

输出

输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。

样例输入

8
300 207 155 300 299 170 158 65

样例输出

6

2.思路分析

这道题本人求解时比较吃力,没有观察出题目体现出的特殊性质;这里先给出本人的原始思路看看有没有想法类似的朋友,后面会给出大佬的思考角度。

2.1 整体思路

由于没想到比较优秀的解法,我使用的是递归暴搜+状态存储结合的方式。根据题目的输入,使用一个导弹任务数组(task)来存储每一颗导弹的高度。个人感觉比较直观的想法是以递归的方式枚举每一个导弹是否拦截,并且在决定是否拦截当前导弹(状态转移)前要确定最大拦截高度是否允许;将每个状态与2个参数绑定,当前面对的导弹和当前允许拦截的最大高度。如果直接使用高度作为状态数组的下标会使存储空间的占用非常大,可以选择使用被拦截的导弹的数组下标来代替(result[26][26])需要高度数值时直接利用下标查找即可。

2.2 状态转移

确定整体思路后,我们在整个result数组中进行递归遍历;入口为result[1][0],即当前面对第一颗导弹最大拦截高度为task[0](这里task[0]要给定一个最大值)。

2.2.1 边界处理

递归处理到最后一枚导弹时,根据高度判断能否拦截再返回即可(题目要求最大值,尽量拦截)。

2.2.2 常规情况

  1. 如果允许拦截的最大高度小于当前导弹高度,带着当前的最大允许高度对下一枚导弹进行判断(递归求解)后返回。
  2. 如果允许拦截的最大高度大于当前导弹高度,我们可以选择不拦截当前导弹也可以拦截。不拦截的情况与上一种情况相同,拦截的话就要将允许拦截的最大高度修改为当前导弹高度。对两种情况分别递归求解,取二者种较大者再返回即可。

3.注意点

  1. 使用递归+状态存储时,要给整个结果数组赋一个特殊的初始状态值;遇到初始值时才需要递归计算,得到结果后立刻修改状态值防止其重复计算。
  2. 遇到直观想法下数组空间需要开得很大时,可以考虑做一些简单的变换;例如本题中将最大拦截高度转换为已拦截的导弹下标。

4.AC代码

问题分析清楚并给出AC代码,代码内容与分析过程完全对应。

#include<iostream>
using namespace std;
int k;
int task[26];
int result[26][26];
int max_2(int n1,int n2)
{
    if(n1>n2)
        return n1;
    else
        return n2;
}
void init()
{
    for(int i=0;i<26;++i)
    {
        for(int j=0;j<26;++j)
        {
            result[i][j] = -1;
        }
    }
    task[0] = 50000;
}
int stop_missile(int now,int lim)
{
    if(result[now][lim]!=-1)
    {
        return result[now][lim];
    }
    if(now==k)
    {
        if(task[lim]>=task[now])
        {
            return result[now][lim] = 1;
        }
        else
        {
            return result[now][lim] = 0;
        }
    }
    if(task[now]>task[lim])
    {
        return result[now][lim] = stop_missile(now+1,lim);
    }
    else
    {
        return result[now][lim] = max_2(1+stop_missile(now+1,now),stop_missile(now+1,lim));
    }
}
int main()
{
    init();
    cin>>k;
    for(int i=1;i<=k;++i)
    {
        cin>>task[i];
    }
    cout<<stop_missile(1,0)<<endl;
}

5.总结

后面无意间发现了大佬yxc的题解,其实本题是一道很常规的动态规划问题-最长下降子序列;郭老师上课也讲了下降子序列这道例题(好吧,我果然菜;没有把他们联系到一起)。熟悉递归后感觉递归是真的超好用,设置好边界和入口其他的事就都交给计算机了。将递归的思路改成迭代可以进一步提高程序的效率,这时候功力就更深一层了。继续努力,欢迎各位大佬指点,也欢迎各位朋友一起讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值