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 常规情况
- 如果允许拦截的最大高度小于当前导弹高度,带着当前的最大允许高度对下一枚导弹进行判断(递归求解)后返回。
- 如果允许拦截的最大高度大于当前导弹高度,我们可以选择不拦截当前导弹也可以拦截。不拦截的情况与上一种情况相同,拦截的话就要将允许拦截的最大高度修改为当前导弹高度。对两种情况分别递归求解,取二者种较大者再返回即可。
3.注意点
- 使用递归+状态存储时,要给整个结果数组赋一个特殊的初始状态值;遇到初始值时才需要递归计算,得到结果后立刻修改状态值防止其重复计算。
- 遇到直观想法下数组空间需要开得很大时,可以考虑做一些简单的变换;例如本题中将最大拦截高度转换为已拦截的导弹下标。
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的题解,其实本题是一道很常规的动态规划问题-最长下降子序列;郭老师上课也讲了下降子序列这道例题(好吧,我果然菜;没有把他们联系到一起)。熟悉递归后感觉递归是真的超好用,设置好边界和入口其他的事就都交给计算机了。将递归的思路改成迭代可以进一步提高程序的效率,这时候功力就更深一层了。继续努力,欢迎各位大佬指点,也欢迎各位朋友一起讨论。