【周末夜校】导弹防御系统——递归算法

湖南大学信息科学与工程学院第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

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值