导弹拦截问题

问题描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:**虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。**某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

一行,为导弹依次飞来的高度

输出格式

两行,分别是最多能拦截的导弹数与要拦截所有导弹最少要配备的系统数

样例输入

389 207 155 300 299 170 158 65

样例输出

6
2

思路详解

首先这个输入就比较坑。我习惯了先输入一个n,然后接下来的1行里面有n个数。这样的输入,得找到边界条件并且正确t统计输入的个数。

//输入
//181 205 471 782 1033 1058 1111
int n=1;
    //把n个数都输入进去
    while(scanf("%d",&a[n])!=EOF){
        if(getchar()=='\n')
            break;//如果输入换行,则退出
        n++;//如果不是,继续输入下一个导弹得高度
    }

接下来分析第二个问题,最多能够拦截导弹的数量。
这相当于1个序列里面,按照先后顺序挑选出最多的元素使得这个挑选出来的序列是非递增的。这也是最经典的最长非递增子序列问题,可用动态规划求解。对于每一个数组元素,可设置从第一个导弹到当前位置能够拦截的最大导弹数目为1,那么从第二个导弹开始往后遍历,如果后面的导弹的高度比前面某一个的高度要低,那么当前拦截导弹的数目可能是前面这一个的拦截数目+1,从而可以得到递推方程b[i]=max{b[i],b[j]+1}(j<=i&&a[j]>=a[i])。

int getLIS(int n)
{
    for(int i=1;i<=n;i++)
        b[i]=1;
    for(int i=2;i<=n;i++){
        for(int j=1;j<=i-1;j++){
            if(a[i]<=a[j]){
                b[i]=max(b[i],b[j]+1);
            }
        }
    }
    int max=b[1];
    for(int i=2;i<=n;i++)
      if(max<b[i])
         max=b[i];
    return max;
}

最后,从得到的b数组中挑选出最大值,即为当前数组的最长非递增子序列的长度。

第三个问题,求需要的最小的导弹拦截系统数目。这里用到贪心算法。贪心策略为,当前导弹来临时,用能够拦截它的拦截高度最低的拦截系统来拦截它,因为这样拦截系统付出的代价最小(导弹拦截系统拦截导弹后,拦截高度变小了,当然是希望用相对拦截高度最低的去拦截)。如所有系统都不能拦截,则增加一个导弹拦截系统。

int minSystem(int n)
{
    m[1]=a[1];//第一个导弹拦截系统
    int k=1;//导弹拦截系统的数目
    for(int i=2;i<=n;i++){
        int flag=0;//用来标记
        for(int j=1;j<=k;j++){
            if(a[i]<=m[j])//说明可以拦截
            {
                if(flag==0)//说明这是可以拦截的第一个系统
                    flag=j;
                if(flag!=0&&m[flag]>=m[j])
                    flag=j;
            }
        }
        if(flag==0)//说明没有系统可以拦截
        {
            k++;
            m[k]=a[i];
        }
        else{
            //可以拦截,更新代价最小的系统
            m[flag]=a[i];
        }
    }
    return k;
}

AC源代码

#include 
#include
#include
using namespace std;
int a[100001];//存放导弹高度
int b[100001];//存放当前最长非递增子序列长度
int m[100001];//存放导弹拦截系统的拦截高度
//求两个数中的最大值
int max(int x,int y)
{
    if(x>=y)
        return x;
    else
        return y;
}
//求一个数组中的最长非递增子序列
int getLIS(int n)
{
	//b[i]表示从第一个元素到第i个元素的最长非递增子序列的长度。
    for(int i=1;i<=n;i++)
        b[i]=1;
    //动态规划
    //b[i]=max{b[i],b[j]+1}(j=a[i])
    for(int i=2;i<=n;i++){
        for(int j=1;j<i;j++){
            if(a[i]<=a[j]){
                b[i]=max(b[i],b[j]+1);
            }
        }
    }
    //求b数组的最大值,即为整个a数组的最长非递增子序列的长度
    int max=b[1];
    for(int i=2;i<=n;i++)
      if(max<b[i])
         max=b[i];
    return max;
}
int minSystem(int n)
{
    m[1]=a[1];//第一个导弹拦截系统
    int k=1;//导弹拦截系统的数目
    for(int i=2;i<=n;i++){
        int flag=0;//用来标记
        for(int j=1;j<=k;j++){
            if(a[i]<=m[j])//说明可以拦截
            {
                if(flag==0)//说明这是可以拦截的第一个系统
                    flag=j;
                if(flag!=0&&m[flag]>=m[j])//如果不是第一个拦截系统但是比拟打算拦截这个导弹的系统的拦截高度还要低
                    flag=j;//更新flag
            }
        }
        if(flag==0)//说明没有系统可以拦截
        {
            k++;//再增加一个拦截系统
            m[k]=a[i];//拦截系统的高度就是当前导弹的高度
        }
        else{
            //可以拦截,更新代价最小的系统
            m[flag]=a[i];
        }
    }
    return k;
}
int main()
{
    int n=0;
    char ch;
    //把n个数都输入进去
    while(scanf("%d",&a[n])!=-1){
        n++;
        ch=getchar();
        if(ch=='\n')
            break;
    }
    cout<<getLIS(n)<<endl;
    cout<<minSystem(n);
    return 0;
}

其他数据

样例输入2:

181 205 471 782 1033 1058 1111

样例输出2:

1
7

导弹拦截问题_第1张图片

你可能感兴趣的:(蓝桥杯C++训练系统)