动态规划--寻找最长递减子序列

昨天C++课上留了三道题,除了C语言本身外都涉及了一些算法。其中第二个问题是这样的:

拦截导弹

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

输入

第一行,输入雷达捕捉到的敌国导弹的数量kk<=25),

第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。

输出

输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。

样例输入

8

300 207 155 300 299 170 158 65

样例输出

6

课堂上没搞出来。老师说用到了动态规划。好吧,高中没学过信息学竞赛。不过没关系,回去翻了翻《算法导论》,基本算弄明白了。其实就是求一串数的最长递减子序列。


问题关键在于,可能的情况组合很多,不适合穷举。所以要利用动态规划(很适合这类问题)。动态规划把问题分成很多子问题,而子问题之间又有关系。子问题不是相互独立的,子问题可能有重叠。这和分治法(典型的比如归并排序)不一样。动态规划的基本想法在于:1.描述最优解结构,就是说最优解是什么样子的,符合什么条件。2.递归定义最优解的值。3.自底向上计算最优解的值。4.由计算的值构造最优解(在这题里没要求,到第三部求出序列长就行了)。


这个问题中比如说要拦第i个导弹。截止到第i个导弹的最长拦截序列肯定包含了前面i-1个导弹中最长的序列。如果不是这样,我们求出的截止到第i个导弹的截序列就不是最长的了。我们用数组D[i]把截止到第i个导弹的最长拦截序列长度存起来。最后计算完i=k(导弹总数)时,整个问题的答案就是数组D[i]的最大值。


如果用一个数组,把前一个最优解对应的导弹编号存下来,最后还可以把被击落的导弹高度按编号输出。

代码:

/*
    设数组A存放导弹高度,D[i]是高度为A[i]是的最优解,S用于记录最优解中导弹被击落的次序
    最优解结构:设在第k个数处的最优解为D(k)。设当前位于第i个数,则在第i个数处的
                最优解D(i)为:在i之前拦下导弹最多的方法的解D(j)加上1。(因为把第i
                个也打下来了,所以加上1)。当然还要保证A[i]<=A[j]
    递归解:D(i)=D(j)+1。j是数组A中位于A[i]左侧,且大于等于A[i]的所有数中D值最大的
            那个。如果A[i]左侧没有找到大于等于它的数,则D[i]=1。
    最终结果:找到数组D中最大的数,这个数就是结果。
*/
#include 
int main(){
    int k;
    scanf("%d", &k);
    int A[k], D[k], S[k];

    for(int i=0; i=A[i] && D[j]>=D[good_position]){
                last_good_solve=D[j];
                good_position=j;
                found=true;
            }

        if(found){
            D[i]=1+D[good_position];
            S[i]=good_position;
        }
        else {
            D[i]=1;
            S[i]=good_position;
        }
    }

    int result=0;   int go_back;//用于向前找被击落的导弹
    for(int i=0; iresult){
            result=D[i];
            go_back=i;
        }
    }

    printf("%d\n", result);

    for(int i=0; i
动态规划--寻找最长递减子序列_第1张图片

你可能感兴趣的:(算法和数据结构)