此文为博主原创,转载时请通知博主,并把原文链接放在正文醒目位置。
题目链接
https://www.luogu.org/problemnew/show/P1020
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
一行,若干个整数(个数少于100000)
输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输入样例#1:389 207 155 300 299 170 158 65
输出样例#1:6 2
分析:
这题想要AC的话只能用单调性来做
博主还用了Dilworth定理
如果不知道Dilworth定理的话下面附上百科链接
https://baike.baidu.com/item/Dilworth%E5%AE%9A%E7%90%86/5489361?fr=aladdin
//第一问很显然的贪心做法,第二问也很显然
如果当前数小于等于当前的最长不上升序列的结束点,那么我们把当前最长的不上升序列长度加一,把当前数作为这个 不下降序列的结束点,不然我们就用二分查找(为什么可以呢?这是因为我们运用了贪心的思想后能保证长度越大的不上升序列结束点越小),试着用当前数去更新长度为x的不上升序列的结束点(又是贪心的思想,只更新长度最长且结束点小于自己的),然后第二问你再反着做就行了(把大于等于改为小于)
小提示:记得数组开大点,不然会RE的qwq
AC代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <string> 5 const int maxn = 100005; 6 int a[maxn], f[maxn]; 7 int main(){ 8 int n; 9 int l, r, mid; 10 while (scanf("%d", &a[++n]) != EOF) continue; 11 n--; 12 f[0] = 7758521; 13 int ans1 = 0; 14 for (int i = 1; i <= n; i++){ 15 if (f[ans1] >= a[i]){ 16 f[ans1 + 1] = a[i]; 17 ans1++; 18 } 19 else { 20 l = 0, r = ans1; 21 while (l < r){ 22 mid = (l + r)/2; 23 if (f[mid] >= a[i]) l = mid + 1; 24 else { 25 r = mid; 26 } 27 } 28 if (l != 0) f[l] = a[i]; 29 } 30 } 31 std::cout << ans1 << std::endl; 32 memset(f, -1, sizeof(f)); 33 int ans2 = 0; 34 for (int i = 1; i <= n; i++){ 35 if (f[ans2] < a[i]){ 36 f[ans2 + 1] = a[i]; 37 ans2++; 38 } 39 else { 40 l = 0; 41 r = ans2; 42 while (l < r){ 43 mid = (l + r)/2; 44 if (f[mid] >= a[i]) r = mid; 45 else { 46 l = mid + 1; 47 } 48 } 49 f[l] = a[i]; 50 } 51 } 52 std::cout << ans2 << std::endl; 53 }