详解_最长不上升子序列(拦截导弹)_动态规划

拦截导弹

时间限制:3000 ms  |  内存限制:65535 KB

难度:3

输入

第一行输入测试数据组数N(1<=N<=10)
接下来一行输入这组测试数据共有多少个导弹m(1<=m<=20)
接下来行输入导弹依次飞来的高度,所有高度值均是大于0的正整数。

输出

输出最多能拦截的导弹数目

样例输入

2
8
389 207 155 300 299 170 158 65
3
88 34 65

样例输出

6
2

描述

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

分析:本题是一个典型的最长子序列问题,(Longest increasing/decreasing subsequence.),属于线性结构上的动态规划问题。需要注意的是,贪心是行不通的,因为这里要保证一定的顺序,而贪心必须要进行排序,这注定会打乱原始顺序,得到的结果通常不能保证是原序列的子序列。(除非原序列是按照严格升降顺序排列的,而这样题目就没有意义了)

如果将每一个导弹的高度看作一个点,则导弹发射的先后顺序及高度之间的关系决定了两个导弹(点)之间是否连通,这样一来本题就变成了DAG上无固定起点和终点的最长路径问题了,直接就可以得到动态转移方程。

解答:设d[t]:以第t个导弹为起点的最长路径,则状态转移方程为; d [ i ]  =  max( d[ i ],d[ j ]+1) ( i<j,missile[ i ]>misslile [ j ],第 i 个导弹必须比第j个导弹先发射,且第i个导弹的高度要高于第j个导弹的高度) ,计算出所有d[i]后,打擂台找到最大的那一个就是答案了。

代码:

#include<bits/stdc++.h>
#define MAX 25
using namespace std;

int  main()
{
    int t,m;
    int missile[MAX];
    int d[MAX];
    cin>>t;
    while(t--)
    {
        cin>>m;
        for(int i = 1 ; i<=m ; i++)
            cin>>missile[i];

        memset(d,0,sizeof(d));

        for(int i = m-1 ; i>0 ; i--)注意这里的i要逆向枚举(由于j>i,试想如果正向枚举会导致计算d[i]时用到的d[j]还没有计算出来为初值0,最终计算结果错误。)
            for(int j = 0 ; j<=m ; j++)
                if(i<j&&missile[i]>missile[j])
                    d[i] = max(d[i],d[j]+1);

        int temp = d[1];
        for(int i = 1 ; i<=m ; i++)
            if(d[i]>temp)
                temp = d[i];
        cout<<temp+1<<endl;
    }
    return 0;
}
//其实循环可以改为,可以减少一定的循环次数
for(int i = m-1 ; i>0 ; i--)
for(int  j = i+1  ; j<=m  ;j++)
if(missile[i]>missile[j])

猜你喜欢

转载自blog.csdn.net/SWEENEY_HE/article/details/82251868