动态规划——洛谷1020_导弹拦截(1999年的)
题目测评网址:戳我打开
题目:
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
一行,若干个正整数最多100个。
输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输入样例#1:
389 207 155 300 299 170 158 65
输出样例#1:
6
2
先给大家一个提示,这里的一个系统可以先不拦截一个导弹,然后等到有可以拦截的导弹是在拦截
这题是《信息学奥赛一本通》的例9.4,先来看看书上是如何解析这道题的:
【算法分析】
第一问即经典的最长不下降子序列问题,可以用一般的DP算法,也可以用高效算法,但这个题的数据规模不需要。
用a[x]表示原序列中第x个元素,b[x]表示长度为x的不下降子序列的长度,。当处理第a[x]时,可查找它可以连接到长度最大为多少的不下降子序列后(即与部分b[x]比较)。假设可以连到长度最大为maxx的不下降子序列后,则b[x]:=maxx+1。b数组被赋值的最大值就是第一问的答案。
第二问用贪心法即可。每颗导弹来袭时,使用能拦截这颗导弹的防御系统中上一次拦截导弹高度最低的那一套来拦截。若不存在符合这一条件的系统,则使用一套新系统。
我的见解:第一问等同于求一个最长下降序列,这里用之前讲到过的方法来分析这题,如果你还不知道这个方法,请访问:动态规划——如何分析一道动态规划的题
发个小广告,如果你正在学习动态规划那么请关注戳我加关注。
状态表达:f[i]表示前i个数的最长下降序列。
状态转移:
for(int i = 0;i<n;i++)
for(int j = 0;j<i;j++)
{
if(num[i]>=num[j]||f[j]+1<f[i])continue;
f[i] = f[j]+1;
}
其中 num为输入的数。
状态数量:n个。
转移代价:O(n)
时间复杂度:O(n^2)
空间复杂度:O(n)(常数忽略)
第二是一个贪心:
举例389 207 155 300 299 170 158 65
需要2个系统。
第一个系统:389 207 155 65
第二个系统:300 299 170 158
这里可以发现,65这个导弹在两个系统都可以捕捉到的情况下属于了第一个系统,再举例:389 207 155 300 299 170 158 65 66 63
需要2个操作系统。
第一个系统:389 207 155 65 64 63
第二个系统:300 299 170 158
这里可以发现,贪心策略是,当一个导弹不能被已有系统拦截是,新开一个系统,当一个导弹可以被多个系统拦截时,选择那个最低值离导弹最近的系统。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
int num[1001],f[1001],p[1001];
std::vector<int> lower;
bool find(int x)
{
std::sort(lower.begin(),lower.end());
for(int i = 0;i<lower.size();i++)
if(lower[i]>x){lower[i] = x;return true;}
return false;
}
int main()
{
int n = 0,ans = -1;
while(scanf("%d",&num[n])!=EOF)n++;
for(int i = 0;i<n;i++)
for(int j = 0;j<i;j++)
{
if(num[i]>=num[j]||f[j]+1<f[i])continue;
f[i] = f[j]+1;
}
for(int i = 0;i<n;i++)ans = std::max(ans,f[i]);
std::cout<<ans+1<<std::endl;
for(int i = 0;i<n;i++)
{
if(find(num[i])==false)
lower.push_back(num[i]);
}
std::cout<<lower.size();
return 0;
}