题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是le 50000≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
11行,若干个整数(个数le 100000≤100000)
输出格式
22行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输入
389 207 155 300 299 170 158 65
输出
6
2
说明/提示
为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分
每点两问,按问给分
思路:
读过题目后发现,第一问考察了最长不上升子序列,第二问需要用到贪心。
这里主要说第一问,我最先想到的求法是这样的:
1 for(int i=1;i<=n;++i) 2 f[i]=1;//初始化 3 for(int i=n;i>=1;--i)//最长不上升子序列 4 for(int j=n;j>i;--j) 5 if(s[i]>=s[j]) 6 f[i]=max(f[i],f[j]+1); 7 int maxn=f[1]; 8 for(int i=1;i<=n;++i) 9 if(maxn<f[i]) 10 maxn=f[i];
朴素的n方算法,但效率较低,只能拿到100分。
求法需要优化,可以看给出的提示,nlogn,我想到了使用二分查找。
在数组f[i]中存放的是第i个数最长不上升子序列的长度,f[ ]数组是单调递增的,故我们可以使用二分来代替第二重循环。
完整代码:
1 #define N 100005 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 int s[N],f[N],dp[N];//f[i]第i个导弹时最多能拦截个数 7 int w; 8 int erfen(int l,int r,int i) 9 { 10 int mid; 11 while (l<r) 12 { 13 mid=(l+r)>>1; 14 if(s[i]<=dp[mid]) l=mid+1; 15 else r=mid; 16 } 17 return l; 18 } 19 int main() 20 { 21 std::ios::sync_with_stdio(false); 22 int n=1,maxh,k; 23 while(cin>>s[n])//读入数据 ctrl z停止 24 ++n; 25 --n; 26 dp[1]=s[1];w=1; 27 for(int i=2;i<=n;++i) 28 { 29 if(s[i]<=dp[w]) dp[++w]=s[i]; 30 else { 31 dp[erfen(1,w,i)]=s[i]; 32 } 33 } 34 cout<<w<<' '; 35 int b=0,ans=0,maxs; 36 while(b<n) 37 { 38 maxs=0x7fffffff; 39 for(int i=1;i<=n;++i) 40 { 41 if(maxs>=s[i] && s[i]>=0) 42 { 43 maxs=s[i]; 44 s[i]=-1; 45 ++b; 46 } 47 } 48 ++ans; 49 } 50 cout<<ans; 51 return 0; 52 }