P1020 导弹拦截
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
一行,若干个正整数最多100个。
输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输入样例#1:
389 207 155 300 299 170 158 65
输出样例#1:
6 2
这道题就是要求一个最长单调不升子序列和一个最长单调上升子序列
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 int a[100005],n=1,l=0; 7 int f[100005]; 8 int main() { 9 while(scanf("%d",&a[n])==1) n++; 10 n--;
求最长不下降子序列
upper_bound(begin,end+1,num);
求不降序列中第一个大于num的位置
返回的是地址(用指针标记)
若不存在,返回end+1的位置
Eg:upper_bound(a+1,a+n+1,target);
若a[1~n]中没有大于target的值,则返回a+n+1
下面代码是求最长不上升子序列的:
由代码可知f数组中的数是不下降的(即最长不下降子序列)
言外之意,相邻两个数可以相等(!!重要!!)
因为一直是大的数字在后面补充
原理:贪心
每次在f数组(拟定的最长不下降子序列)中找比当前值大的数,然后将其替换为当前值
因为数组里的每个数越小越好,(越小越能扩展的更长)
由此,贪心成立
f数组的长度即为最长不下降子序列的长度
注意:l要从0开始(第一个数要填充在第一位上)
若要求最长不上升子序列就要把原数组倒过来处理
1 for(int i=n; i>=1; i--) { 2 int *t=upper_bound(f+1,f+l+1,a[i]); 3 if(t==f+l+1) l++;//结尾判定 4 *t=a[i]; 5 } 6 cout<<l<<endl; 7 memset(f,0,sizeof(f)); 8 l=0;
遗憾,本题要求的是单调上升子序列,因此要改!
改成lower_bound(f+1,f+l+1,num):从区间[f+1,f+l+1)中取大于等于num的第一个数的地址
然后把f数组中!!等于!!小于的覆盖了(所以相等的只出现一次)
1 for(int i=1; i<=n; i++) { 2 int t=lower_bound(f+1,f+l+1,a[i])-f;// !!t保存的是地址!! 3 if(t==l+1) l++;//这里是说如果f数组中的所有数都比a[i]小,那么将这个数存到向后的一个位置 4 f[t]=a[i];//把f数组中的数修改,等于的直接覆盖,小于的覆盖第一个比它大的数,大于所有数的放到向后的一个位置 5 }//for循环之后f数组的长度即为最长的单调不下降子序列一种情况! 6 cout<<l<<endl; 7 }