拦截导弹
时间限制: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])