湖南大学信息科学与工程学院第15届生涯规划节周末夜校之C++讲座
(Date:20201205,面向2020级大一新生)
Description
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。
Input
每组输入有两行,
第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),
第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。
Output
每组输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。
Sample Input
8
300 207 155 300 299 170 158 65
Sample Output
6
递归问题的一般方法
1、找到递归开始状态,即递归入口;
2、调用递归函数;
3、判断递归参数合法性,制定“由错误导致的”递归终止条件;
4、为合法的参数执行运算操作;
5、找到递归边界并判断,制定“由递归边界导致的”递归终止条件;
6、确定下一步的递归方向,用更新的参数调用递归;
7、函数返回上一层递归前,还原在本层递归中改变的参数。
本题思路和分析
每个导弹可以选择打或者不打,如果选择打了第一发300的,那么如果再选打第二发207,则后续只能打到155,65两个导弹,这样总共只能打4个导弹。但是如果打了第一发300以后,第二第三的207,155都不打,那么可以继续打到第四发300的导弹,而从第5发导弹开始高度都是递减的,因此后续的导弹都能打到,这样最优解就能打到6发导弹。
1、找到递归开始状态,即递归入口:arr作为导弹高度的存储位置,从第一发导弹开始计算,即arr[1]。
2、调用递归函数:每一发导弹可以打或者不打。因此在主函数里面调用了两次defend。defend有两个参数,第一个参数cur代表当前考虑编号几的导弹,def代表打还是不打,1是打,0是不打。
3、判断递归参数合法性,制定“由错误导致的”递归终止条件:本题中不存在该类情况。
4、为合法的参数执行运算操作:curheight参数记录了当前能打的最高位置,且初始为-1。如果def标记位为1,且curheight比arr[cur](当前考虑的导弹的高度)要高,就可以执行打的操作。如果curheight=-1说明一发还没有打过,因此可以直接打。打完导弹以后将该导弹的高度更新为下一次可打的最高高度,并且curhit(已打数量)加一。
5、找到递归边界并判断,制定“由递归边界导致的”递归终止条件:如果cur=n,即当前已经考虑到最后一个导弹,就判断curhit和maxn的关系,如果curhit(已打数量)比存储的打导弹数量最大值要多,那么更新max为当前的已打数量curhit。
6、确定下一步的递归方向,用更新的参数调用递归:下一层搜索时,导弹序列cur+1,搜索方向为1(打下一个)或0(不打下一个)。
7、函数返回上一层递归前,还原在本层递归中改变的参数:本层中对于curhit(已打数量)和curheight(可打高度)进行了更新,在返回上一层之前,将这两个参数用tmp1,tmp2两个缓存位强制更新(避免相同高度重复计算),还原成未考虑当前导弹之前的情况,并返回上一层递归,重新对当前的导弹进行考虑(1或者0)。
参考代码和注释
#include<iostream>
using namespace std;
int n=0;
int flag=0;
int maxn=0;
int curhit=0;
int curheight=-1;
int arr[1001];
void defend(int cur,int def)
{
int tmp1=curheight;
int tmp2=curhit; //存储本轮调用之前已经打的导弹数量和高度,函数返回前还原变量
if(def==1) //如果要打则打,不打则defend函数无额外操作
{
if(curheight>=arr[cur] || curheight==-1) //如果当前可以打,或者是第一次打
{
curhit++; //已打数量加一
curheight=arr[cur]; //后续高度不可超过本次已打高度
}
}
if(cur==n) //递归终止条件,已经考虑所有导弹
{
if(curhit>maxn)
maxn=curhit; //比对已打导弹数量和最大值
return; //返回上一层递归
}
defend(cur+1,1); //打完这一个导弹,考虑下一个导弹打还是不打。考虑结束后,函数在该位置返回继续执行。
defend(cur+1,0); //考虑没有打的导弹
curheight=tmp1;
curhit=tmp2; //函数至此返回上一层,因此要还原本次已打的导弹和高度,进行下一轮递归
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>arr[i];
}
defend(1,1); //打或不打都可以
defend(1,0);
cout<<maxn<<endl;
}
拓展阅读
本题的dp解法:https://blog.csdn.net/qq_41708792/article/details/105850952