话说上一篇我们已经讲到了动态规划,那么我们今天就来继续讲一下动态规划的题目。
这次当然还是一本通上的题目了。
这个是1999年Noip的原题,也是另一道拦截导弹的进阶。
【题目描述】
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
【输入】
输入导弹依次飞来的高度。
【输出】
第一行:最多能拦截的导弹数;
第二行:要拦截所有导弹最少要配备的系统数。
【输入样例】
389 207 155 300 299 170 158 65
【输出样例】
6
2
题目分析:
这一道题分两个问,一个是最多的拦截数(动态规划),另一个就是贪心。
值得注意的是,这道题没有说明要输入多少个导弹,所以我们选择另一种输入方式:
while(cin>>n)
接着呢,我们先来解决动态规划。首先将dp数组的每一个初始化为1。接着,我们用双层循环遍历dp数组。而状态转移方程也就有了:
dp[i]=max(dp[j]+1,dp[i]);
当然,我们最后还要求dp数组中的最大值。
maxn=max(maxx,dp[i]);
到了这里,动态规划部分就写完了,先把从输入到动态规划的代码放上来:
#include <bits/stdc++.h>
using namespace std;
int a[100001];
int v[100001];
int n=1;
int ans=0;
int dp[100001];
int main()
{
while(cin>>a[n]) n++;
n--;
int maxn=-114514;
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
if(a[j]>=a[i])
dp[i]=max(dp[j]+1,dp[i]);
maxn=max(maxn,dp[i]);
}
cout<<maxn<<endl;
}
接着就是贪心了。贪心的话之前应该没讲过,所以这部分详细点(bushi)。首先,我们要找到我们的贪心策略。首先先开一个系统,如果可以拦截且没有被拦截,那么拦截数量+1,标记为已经拦截。同时也要变化高度。在这道题中,这部分的代码就是:
if(a[i]<=h and v[i]==0)
{
cnt++;
v[i]=1;
h=a[i];
}
代码:
最后的最后,放上AC的代码。
(你怎么还复制,为什么不自己打!)
#include <bits/stdc++.h>
using namespace std;
int a[100001];//每一枚导弹的高度
int v[100001];//是否拦截
int n=1;//导弹数量
int ans=0;//存储最少需要多少套系统
int cnt=0;//拦截导弹数
int dp[100001];
int main()
{
while(cin>>a[n]) n++;//输入
n--;
int maxn=-114514;
for(int i=1;i<=n;i++)
{
dp[i]=1;//初始化为1
for(int j=1;j<i;j++)
if(a[j]>=a[i])//第j枚导弹的高度比第i枚大
dp[i]=max(dp[j]+1,dp[i]);//状态转移方程
maxn=max(maxn,dp[i]);//求dp数组的最大值
}
cout<<maxx<<endl;//输出拦截的最大数量
while(cnt<n)//拦截数量小于总数
{
ans++;//一套系统
int h=114514;//系统初始高度
for(int i=1;i<=n;++i)//遍历每一枚导弹,看看哪些可以拦截
if(a[i]<=h and v[i]==0)//可以拦截及没有被拦截
{
cnt++;//拦截的数量
v[i]=1;//标记为已拦截
h=a[i];//高度下降
}
}
cout<<ans<<endl;//输出需要几个系统
return 0;//一万年一见的return 0
}