zoukankan      html  css  js  c++  java
  • Luogu P1020 导弹拦截

    题目地址

    最多能拦截的导弹数为最长不上升子序列的长度

    根据$dilworth$定理(偏序集所能划分成的最少的全序集的个数等于最大反链的元素个数)可以得到第二问的答案就是最长上升子序列的长度。

    $ n le 100000 $ 因此$O(n^{2})$的算法不能得全分,这里讲解$O(nlog{n})$的算法。

    假设我们要求某序列的最长不上升子序列的长度。

    我们设置两个数组,$a$与$c$,$a[i]$代表序列的第$i$个元素,$c[i]$表示长度为$i$的不上升子序列的最后一位的最大值。

    再设置一个变量$len$,为$c$数组的长度。

    从1到$n$扫描数组$a$

    检查$a[i]$是否小于等于$c[len]$,如果是,则$c[++len] = a[i]$

    如果否,那么寻找$c$中第一个小于$a[i]$的数,并将其替换成$a[i]$。
    如果按照朴素的查找方法,寻找第一个小于$a[i]$的数所需时间复杂度为$O(n)$,这样算法整体复杂度仍为$O(n^2)$,我们需要对其进行优化。

    我们会发现,因为我们找的是第一个小于$a[i]$的数,因此$c$数组是满足二分性的,可以使用二分查找。这样一次查找的时间复杂度就降为了$O(log{n})$,整体的时间复杂度为$O(nlog{n})$。

    我们要尽可能让每一个$c[i]$尽量大,因为不下降子序列的最后一位越大,后面能选择的数就越多。

    函数lower_bound upper_bound

    lower_bound与upper_bound的作用就是二分查找,lower_bound可以找到第一个大于等于某个数的数,upper_bound可以找到第一个大于某个数的数,她们的返回值为查找到的数的指针,如需获得这个数的位置,可以用获得的指针减去数组开头的指针。如需获得这个数,直接设置一个指针存下来就行

    $eg:$
    我们要在数组$b$中找到第一个大于等于$x$的数$p$。($b$的长度为$m$)

    *p = lower_bound(b,b + m,x);

    则$*p$就是这个数

    p = lower_bound(b,b + m,x) - b;

    则$b[p]$就是这个数。

    upper_bound的使用方法同上

    注意,lower_bound和upper_bound只能在升序序列里使用。

    那么我们想要在一个降序序列里找到第一个小于等于或小于$x$的数该怎么办呢?
    我们可以修改一下lower_bound或upper_bound,加上$cmp$函数,或使用$greater<int>()$

    $eg:$

    *p = lower_bound(b,b + m,x,greater<int>);
      
      
    bool cmp(const int& a,const int& b) {
          return a > b;
    }
    
    *p = lower_bound(b,b + m,x,cmp);

    $Code:$

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int MAXN = 100010;
    
    int len1,len2,n;
    int a[MAXN],f1[MAXN],f2[MAXN];
    
    int main() {
        while(cin >> a[++n]);n--;
        len1 = len2 = 1;
        f1[1] = f2[1] = a[1];
        for(int i = 2; i <= n; i++) {
            if(a[i] <= f1[len1]) {
                f1[++len1] = a[i];
            }
            else {
                int p = upper_bound(f1 + 1,f1 + len1 + 1,a[i],greater<int>()) - f1;
                f1[p] = a[i];
            }
            if(a[i] > f2[len2]) {
                f2[++len2] = a[i];
            }
            else {
                int p = lower_bound(f2 + 1,f2 + len2 + 1,a[i]) - f2;
                f2[p] = a[i];
            }
        }
        cout << len1 << endl << len2 << endl;
        return 0;
    }
  • 相关阅读:
    2019年4月
    20190423
    20190419
    20190418
    20190417
    free命令详解(转载)
    https改造过程中的一个坑
    GitLab 实现代码自动部署(转载自https://segmentfault.com/a/1190000011561808)
    js和php写日历
    shell递归遍历目录的方法
  • 原文地址:https://www.cnblogs.com/kjd123456/p/12446915.html
Copyright © 2011-2022 走看看