• 树状数组求最长上升子序列


    先说最简单的做法:

    一种是最常见的dp方法,令f[i]表示以A[i]元素结尾的LIS长度,那么,F[i]=max{F[j]+1) 其中1<=j<i,A[j]<A[i],边界是初始化F[i]=1,复杂度O(n^2)。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
    
    int n, a[1005], f[1005];
    int main () {
      scanf("%d", &n);
      FOR(i, 1, n) scanf("%d", &a[i]), f[i] = 1;
      FOR(i, 1, n)
        FOR(j, 1, i - 1)
          if (a[j] < a[i])
            f[i] = max(f[i], f[j] + 1);
      int ans = 0;
      FOR(i, 1, n)
        ans = max(ans, f[i]);
      printf("%d
    ", ans);
      return 0;
    }

    另一种是有些贪心思想,利用二分:

    如果子序列长度相同,那么最末尾的元素越小,就越有优势,于是对于长度相同的子序列,我们总是用更小的来替换。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int inf=0x3f3f3f3f;
    int a[1005], n;
    
    int main() {
        for (int i = 0; i < 1005; ++i)
            a[i] = inf;
        scanf("%d", &n);
        for (int i = 0, t; i < 1005; ++i) {
            scanf("%d", &t);
            *lower_bound(a, a + n, t) = t;
        }
        printf("%d
    ", lower_bound(a, a + n, inf) - a);
        return 0;
    }

    另一种,用树状数组,这种方法和第一种方法思路是一致的,只不过在寻找比自己小的元素中,最长的那个LIS的过程,利用了树状数组来加速。之前的思想是:当前元素的LIS是位置在自己之前的,且LIS是最大的那个值+1。先不论树状数组是怎么工作的,对于原始数组v,复制一个数组a来排序,之后遍历数组v。这里由于是按顺序遍历v的,所以对于v[i],先操作的元素肯定位置上在v[i]之前。

    然后,对于v[i],得到其在a中的位置p,想办法得到在p之前的LIS长度是多少(就是操作query(p-1)),假设是q,那么ans=max(ans, q+1)。这里,由于对于v[i]实际操作的是p,p是v[i]值大小的相对位置,于是又满足:在位置p前面的那些元素,必然大小是<v[i]的,综上,的确可以同意:这和之前LIS解法一的思想是一致的了。

    现在的要讲树状数组怎么工作,即如何完成上面的”想办法“,无非就是要完成查询位置p之前的最大值(区间最大值),以及对于当前元素v[i]和其在a中位置的p,得到LIS后更新位置p之前的LIS。以往BIT总是用来求区间和,其实这只是它的一个应用,说到底它是一个数据结构,方法就是将sum和add的加法操作变成最值操作。  理解到这儿:其实,这完全可以用线段数来实现,只不过这里要处理的区间恰好是[1, p],即恰好一定是从1开始的,用树状数据能简单解决,就不必用线段数了。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
    #define maxN 10005
    int n, L, v[maxN], a[maxN], T[maxN];
    
    void update(int i, int x) { 
      for (; i <= L; i += i & -i)
        T[i] = max(T[i], x);
    }
    int query(int i) {
      int ans = 0;
      for (; i; i -= i & -i)
        ans = max(ans, T[i]);
      return ans;
    }
    int main () {
      scanf("%d", &n);
      FOR(i, 0, n - 1) scanf("%d", &v[i]), a[i] = v[i];
      sort(a, a + n);
      L = unique(a, a + n) - a;
      int ans = 1, t;
      FOR(i, 0, n - 1) {
        int p = lower_bound(a, a + L, v[i]) - a + 1;
        t = query(p - 1) + 1;
        ans = max(ans, t);
        update(p, t);
      }
      printf("%d
    ", ans);
      return 0;
    }
  • 相关阅读:
    $ [Contest #4]$求和 思博题
    洛谷$P1864 [NOI2009]$二叉查找树 区间$dp$
    洛谷$P4045 [JSOI2009]$密码 $dp$+$AC$自动机
    $bzoj2560$ 串珠子 容斥+$dp$
    洛谷$P1600$ 天天爱跑步 树上差分
    $loj526 [LibreOJ eta Round #4]$ 子集 图论
    $CF888G Xor-MST$ 最小生成树
    $bzoj4152 The Captain$ 最短路
    洛谷$P3645 [APIO2015]$雅加达的摩天楼 最短路
    $bzoj4722$ 由乃 搜索
  • 原文地址:https://www.cnblogs.com/Rosebud/p/9845935.html
Copyright © 2020-2023  润新知