洛谷P1020 导弹拦截【单调栈】

题目https://www.luogu.org/problemnew/show/P1020

题意:

给定一些导弹的高度。

一个导弹系统只能拦截高度不增的一系列导弹,问如果只有一个系统最多能拦截多少导弹。

再问,如果要拦截所有导弹最少需要多少系统。

思路:

对于第一个问题其实就是找整个序列中的最长不升子序列。

对于第二个问题就是找整个序列中的最长上升子序列。因为当有一个高度大于前面的高度时一个系统就搞定不了了。

最长上升子序列用动态规划是可以做的,但是这题会卡。

$O(N^2)$的动规做法是,$dp[i] = max(dp[i], dp[j] + 1),if height[i] > height[j]$

还有一种$O(NlogN)$的做法。这时我们维护一个单调栈。数是从小到大排序的,对于新的一个数,如果比栈顶的元素大,就直接加入栈中。

如果新的数比栈顶要小,我们就在栈中二分查找一个最小的大于新的数的数替换掉。

因为对于已经选好了的序列,我们替换前面的数的话并不会影响答案的长度。但是用更小的数替换大的数将有更大的机会让加入的数变多。

而二分找这样的数也非常简单,使用STL里的upper_bound()和lower_bound()就可以解决了。

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。

lower_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。

upper_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<map>
 4 #include<set>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<cmath> 
 9 #include<stack>
10 #include<queue>
11 #include<iostream>
12 
13 #define inf 0x7fffffff
14 using namespace std;
15 typedef long long LL;
16 typedef pair<string, string> pr;
17 
18 const int maxn = 1e5 + 5;
19 int height[maxn];
20 int n = 0;
21 int incre[maxn], decre[maxn];
22 int len1 = 0, len2 = 0;
23 
24 int main()
25 {
26     while(scanf("%d", &height[n++]) != EOF);
27     for(int i = 0; i < n; i++){
28         if(height[i] > incre[len1]){
29             incre[++len1] = height[i];
30         }
31         else{
32             int j = lower_bound(incre, incre + len1 + 1, height[i]) - incre;
33             incre[j] = height[i];
34         }
35         
36         if(height[i] <= decre[len2]){
37             decre[++len2] = height[i];
38         }
39         else{
40             int j = upper_bound(decre, decre + len2 + 1, height[i], greater<int>() ) - decre;
41             decre[j] = height[i];
42         }
43     }
44     
45     
46     printf("%d\n%d\n", len2, len1);
47     return 0; 
48 }

 

posted @ 2019-05-03 14:01  wyboooo  阅读(229)  评论(0编辑  收藏  举报