导弹拦截问题
问题重现:
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
问题一:计算这套系统最多能拦截多少导弹?
问题二:如果要拦截所有导弹最少要配备多少套这种导弹系统?
第一问分析:
一套系统最多能拦截多少导弹,很明显此问题实际是要求在导弹依次飞来的高度序列中寻找一个最长非连续非递增子串。
第一问动态规划:
子问题状态定义:f[i]表示前i个导弹拦截了导弹i可以最多拦截到的导弹数目。
状态转移方程为:
f[i] = max{f[k]}+1 【存在k=0,1,…i-1中的至少一个h[k]>=h[i]】
f[i] = 1 【对于任意k=0,1,…i-1都有h[k]>h[i]成立】
第一问证明:
①存在k=0,1,…i-1,只要有一个h[k]>=h[i]成立:
这时说明,击中第k个后还可以击中第i个,故而只需要从前面找到最大的,f[i] = max{f[k]}+1
②对于任意k=0,1,…i-1,h[k]>h[i]都成立
这时说明,第i个是至今为止出现的最大的数,也就有f[i]=1
第一问优化:
在k=0,1,…i-1中,搜索时可以从i-1,i-2…1,0倒序查找,找到第1个满足h[k]>=h[i]即可。
再次证明:假设第i个向前倒序搜索时找到的是第m个,同样对于第m个,它的上一个n也必然都有h[i]<=h[m]<=h[n]…,依次组成一个非连续非递增子序列
第二问分析:
由第一问,现在反过来想,如果有一个如同高度序列为1、2、3的问题,显然一套导弹系统最多只能拦截其中一个。
再来看这个高度序列:1、2、3、2、4、6、1,这时不难抽出一个高度递增的序列1、2、3、4、6,同样一套拦截系统最多还是只能拦截其中一个。
还来看一个高度序列:8、1、2、3、2、4、5,这时还是可以抽出一个高度递增的序列1、2、3、4、5,而8可以在击中它的同时还可以击中1~5之间的任意一个,但也仅限于此。
故而不难得出此问题实际是要求在导弹依次飞来的高度序列中获得一个最长非连续递增子序列的长度。
第二问其他同问题一
#include<stdio.h>
#define SIZE 3
/*递归计算最长非连续非递增子串(穷举法)*/
void maxSubstr(int i,int max,int dLen,int *des,int *res,int *maxLen){
int k,count;
if(i>=dLen){
for(k=0,count=0;k<dLen;k++){
if(res[k]==1){
count++;
printf("%d ",des[k]);
}
}
if(*maxLen<count){
*maxLen=count;
}
printf("\n");
return;
}
res[i]=0;
maxSubstr(i+1,max,dLen,des,res,maxLen);
if(des[i]<=max){
res[i]=1;
maxSubstr(i+1,des[i],dLen,des,res,maxLen);
}
}
void main(){
int des[SIZE]={12,13,8};
int res[SIZE];
int maxLen=0;
maxSubstr(0,des[0],SIZE,des,res,&maxLen);
printf("maxLen=%d\n",maxLen);
}