题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是\le 50000≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
1行,若干个整数(个数≤100000)
输出格式
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入
389 207 155 300 299 170 158 65
输出
6
2
众说周知,导弹拦截这个题呢有两种靠谱的写法。一种是动规,时间复杂度为O(N^2)。还一种就是贪心,时间复杂度为O(NlogN)。
动规: O(N^2)
这个题我们看成求最长下降子序列(一个系统最多拦截的导弹数)和最长上升子序列(最多需要几个系统)。
一般这种求最长子序列的,我们可以容易列出转移方程:
dp[i]=max(dp[i],dp[j]+1);
那么上升和下降就是一个a[i]>a[j] 和一个a[i]<=a[j]了。
上代码:
#include "bits/stdc++.h"
using namespace std;
int n,a[100010],dp[100010],dpp[100010],ma,maa;
int main()
{
while(cin>>a[++n]);
n--;
for(int i=1;i<=n;i++)
{
dp[i]=1;dpp[i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if(i==j) continue;
if(a[i]<=a[j])
dp[i]=max(dp[i],dp[j]+1);
if(a[i]>a[j])
dpp[i]=max(dpp[i],dpp[j]+1);
}
}
for(int i=1;i<=n;i++)
{
ma=max(ma,dp[i]);
maa=max(maa,dpp[i]);
}
cout<<ma<<endl<<maa;
}
ps:由于每个导弹只有一发,所以if(i==j) continue;
贪心: O(NlogN)
我们可以借助lower_bound和upper_bound函数
对于系统拦截的最多导弹数,如果导弹不高于当前拦截高度则拦截,否则从已拦截的导弹中去除过低的导弹,将当前导弹拦截!
if(dp[len1]>=a[i]) dp[++len1]=a[i];
else *upper_bound(dp+1,dp+1+len1,a[i],greater<int>())=a[i];
而对于系统数,则刚好相反
if(dpp[len2]<a[i]) dpp[++len2]=a[i];
else *lower_bound(dpp+1,dpp+1+len2,a[i])=a[i];
于是:
#include "bits/stdc++.h"
using namespace std;
int a[100010],dp[100010],dpp[100010],n;
int main()
{
while(cin>>a[++n]);
n--;
int len1=1,len2=1;
dp[1]=dpp[1]=a[1];
for(int i=2;i<=n;i++)
{
if(dp[len1]>=a[i]) dp[++len1]=a[i];
else *upper_bound(dp+1,dp+1+len1,a[i],greater<int>())=a[i];
if(dpp[len2]<a[i]) dpp[++len2]=a[i];
else *lower_bound(dpp+1,dpp+1+len2,a[i])=a[i];
}
cout<<len1<<endl<<len2;
}
ps:因为是下降序列,所以upper_bound要加greater< int >()。