luogu1020 导弹拦截(NOIP1999提高组第1题)
时空限制 1000ms/128MB
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
1行,若干个整数(个数≤100000)
输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输入样例#1:
389 207 155 300 299 170 158 65
输出样例#1:
6
2
说明
为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分
每点两问,按问给分
分析
本题输入可以用如下代码
int n=0,x;
while (cin>>x) a[++n]=x;
输入完,按Ctrl+Z 结束输入,提交没有问题。
我下面代码给的是另外一种输入,用字符流。
第一问:最大不上升子序列
第二问可以贪心,还可以最大上升子序列
代码
法一:朴素解法 O(n^2) 100分
#include<iostream> //O(n*n)
#include<algorithm>
using namespace std;
const int N = 100005;
int a[N],f[N];
int main(){
int x,n=0;
while (cin>>x) a[++n]=x;
fill(f,f+N,1);
for (int i=2; i<=n; i++) //最大不上升子序列
for (int j=1; j<i; j++)
if (a[j]>=a[i]) f[i]=max(f[i],f[j]+1);
int ans=f[1];
for (int i=2; i<=n; i++) ans=max(ans,f[i]);
cout<<ans<<endl;
fill(f,f+N,1);
for (int i=2; i<=n; i++) //最大上升子序列
for (int j=1; j<i; j++)
if (a[j]<a[i]) f[i]=max(f[i],f[j]+1);
ans = f[1];
for (int i=2; i<=n; i++) ans=max(ans,f[i]);
cout<<ans<<endl;
return 0;
}
法二:二分优化 O(nlogn) 200分
#include<iostream> //O(nlogn)
#include<string>
#include<sstream>
#include<algorithm>
using namespace std;
const int N = 100005;
int a[N],f[N];
int main(){
string s;
getline(cin,s);
istringstream is(s);
int x,n=0,k;
while (is>>x) a[++n]=x;
f[1]=a[1]; k=1;
for (int i=2; i<=n; i++) //最大不递增子序列
if (a[i]<=f[k]) f[++k]=a[i]; // <=子序列最后一个值,就加在后面
else f[upper_bound(f+1,f+k+1,a[i],greater<int>())-f]=a[i];
cout<<k<<endl;
f[1]=a[1]; k=1;
for (int i=2; i<=n; i++) //最大递增子序列
if (a[i]>f[k]) f[++k]=a[i]; // >递增子序列最后一个值,就加在后面
else f[lower_bound(f+1,f+k+1,a[i])-f]=a[i]; //替换第一个>=a[i]的值
cout<<k<<endl;
return 0;
}