P1020 导弹拦截

来源:互联网 发布:中世纪纪录片知乎 编辑:程序博客网 时间:2024/05/18 02:14

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是不大于50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入输出格式

输入格式:

一行,若干个整数(个数少于100000)

输出格式:

2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入输出样例

输入样例#1: 复制
389 207 155 300 299 170 158 65
输出样例#1: 复制
62

说明

为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分

每点两问,按问给分


首先根据dilworth定理,最少的最长不下降序列个数等于最长上升序列的长度(反过来也成立),证明略。

那就是经典的求最长上升(不下降)序列的问题了。

这里给出O(nlogn)的做法

以最长不下降序列为例,即第一问

设f【x】为长度为x的最长不下降序列的最后一位最大值

那么可以得到 if (f【x -1】 >= a【i】) f【x】 = max(f【x】,a【i】)

可以看出f【x】是 单调递减的(这非常关键)

那么因为具有单调性,就可以通过二分查找的方式找到应该更新的位置,就可以在O(nlogn)的复杂度上完成算法了。

求最长上升序列也是同理,>= ——> <   ,  max ——> min就可以了。

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<queue>#include<vector>#include<stack>using namespace std;const int MAXN = 100000;const int INF = 50010;typedef long long LL;typedef double DB;inline int get(){char c;while((c = getchar()) < '0' || c > '9');int cnt = c - '0';while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';return cnt;}int a[MAXN + 10],tot = 0; int f[MAXN + 10],len;int main(){#ifdef lwyfreopen("1.txt","r",stdin);/*#elsefreopen(".in","r",stdin);freopen(".out","w",stdout);*/ #endiftot = 0;while(scanf("%d",&a[++tot]) != EOF); tot --; //memset(f,-1,sizeof(f));f[0] = INF; len = 1; f[len] = a[1];for(int i = 2; i <= tot; i ++){int l = 0,r = len;while(r - l > 1){int mid = l + r >> 1;if(a[i] <= f[mid]) l = mid;else r = mid;}while(a[i] <= f[l]) l ++;f[l] = a[i];len = max(l,len);} printf("%d ",len);for(int i = 1; i <= tot; i ++) f[i] = INF;f[0] = 0; len = 1; f[len] = a[1];for(int i = 2; i <= tot; i ++){int l = 0,r = len;while(r - l > 1){int mid = l + r >> 1;if(f[mid] < a[i]) l = mid;else r = mid;}while(f[l] < a[i]) l ++;f[l] = a[i];len = max(l,len);}printf("%d",len);return 0;}