题目描述 Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入描述 Input Description
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)
输出描述 Output Description
输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例输入 Sample Input
389 207 155 300 299 170 158 65
样例输出 Sample Output
6
2
数据范围及提示 Data Size & Hint
导弹的高度<=30000,导弹个数<=20
类型:dp 难度:1.5
题意:转化为下列问题:给出一个序列,求最长递减子序列长度,若每次求出最长递减子序列都将序列中所有数删除,求需要删除几次。
分析:转化后,变成了经典的最长递减子序列问题,即dp[i]表示以i为结尾的最长递减子序列长度,a[i]表示第i位的数,递推方程:dp[i] = max(dp[j]+1),0<=j<i,且满足a[i]<a[j],即找到i之前大于i的并且最大的数+1。最后遍历所有dp[i],找到最大的,就是最长递减子序列。
至于第二个问题,需要每次将属于最长递减子序列的数删除,那么用一个pre数组,pre[i]记录递减序列中i的前驱,然后,在每次找到最长递减子序列后,将该序列逐个删除。
ps:注意一个细节,因为输入给的是一个行数,但是没有给个数,用c++读入的话,先将其用geiline读入string中,然后将这个string读入到stringstream中,再将其读入到vector中。
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<sstream>
using namespace std;
int mmax(int a,int b)
{
return a>b?a:b;
}
int main()
{
int i,j,dp[30],pre[30];
vector<int> a;
string s;
getline(cin,s);
stringstream ss(s);
int t;
while(ss>>t)
{
a.push_back(t);
}
int longest = 0, k;
for(k=0; !a.empty(); k++)
{
int longi=0;
memset(dp,0,sizeof(dp));
memset(pre,-1,sizeof(pre));
dp[0] = 1;
for(i=1; i<a.size(); i++)
{
int maxn = 0;
for(j=0; j<i; j++)
{
if(a[j]>a[i] && dp[j]>maxn)
{
maxn = dp[j];
pre[i] = j;
}
}
dp[i] = maxn+1;
if(dp[i]>dp[longi]) longi = i;
if(dp[i]>longest) longest = dp[i];
}
while(longi>=0)
{
a.erase(a.begin()+longi);
longi = pre[longi];
}
}
cout<<longest<<endl;
cout<<k<<endl;
}