链接:P1020
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式:
1行,若干个整数(个数≤100000)
输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入样例#1:
389 207 155 300 299 170 158 65
输出样例#1:
6
2
分析:
对于第一问:就是求最长非上升子序列的长度
而对于第二问:就是求最长非上升子序列的个数(将原序列全部拆成最长非升子序列最少有多少个)
由Dilworth定理可得 (定义看不懂,直接摆结论 ) :
在一个序列中,最长下降子序列的个数就等于其最长非下降子序列的长度
最长非下降子序列的个数就等于其最长下降子序列的长度
同理,最长上升子序列的个数就等于其最长非上升子序列的长度
最长非上升子序列的个数就等于其最长上升子序列的长度
所以对于第二问,只需要求最长上升子序列(严格递增) 的 长度即可。
此外,由于时间要求,应用二分法来解决。
以下代码:
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=100010;
bool cmp(const int &a,const int &b)
{
return a>b;
}
int main()
{
int a[maxn],d[maxn];
int N=1,len;
while(scanf("%d",&a[N])!=EOF)
N++;
N--;
//求最长非上升子序列的长度
len=1;
d[1]=a[1];
for(int i=2;i<=N;i++)
{
if(d[len]>=a[i])
d[++len]=a[i];
else
*upper_bound(d+1,d+len+1,a[i],cmp)=a[i];
}
printf("%d\n",len);
//求最长上升子序列的长度(即最长非上升子序列的个数)
len=1;
d[1]=a[1];
for(int i=2;i<=N;i++)
{
if(d[len]<a[i])
d[++len]=a[i];
else
*lower_bound(d+1,d+len+1,a[i])=a[i];
}
printf("%d",len);
return 0;
}