zoukankan      html  css  js  c++  java
  • HihoCoder 1068 RMQ-ST算法+BIT

    以前都是用的BIT或者线段树(前者多一些)。

    对于ST(Sparse Table),在求倍增or公共祖先(LCA)时见过,说明还有其他用处,所以还是学习一下。

    首先是预处理,用动态规划(DP)解决。

    设A[i]是要求区间最值的数列,F[i, j]表示从第i个数起连续2^j个数中的最大值。(DP的状态)

    例如:

    A数列为:3 2 4 5 6 8 1 2 9 7

    F[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。同理

                                 F[1,1] = max(3,2) = 3,

                                 F[1,2]=max(3,2,4,5) = 5,

                                 F[1,3] = max(3,2,4,5,6,8,1,2) = 8;

    并且我们可以容易的看出F[i,0]就等于A[i]。(初始值

    我们把F[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从 i 到i + 2 ^ (j - 1) - 1为一段,i + 2 ^ (j - 1)到i + 2 ^ j - 1为一段(长度都为2 ^ (j - 1))。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。F[i,j]就是这两段各自最大值中的最大值。于是我们得到了状态转移方程

                            F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])。

    实则就是倍增的思想,所以循环时,'长度'为第一循环

    void RMQ(int n) //预处理->O(nlogn)
    {
    for(i=1;i<=n;i++) a[i][0]=num[i];
    for(int j = 1; j < 20; ++j) for(int i = 1; i <= n; ++i) if(i + (1 << j) - 1 <= n) { a[i][j] = max(a[i][j - 1], a[i + (1 << (j - 1))][j - 1]); a[i][j] = min(a[i][j - 1], a[i + (1 << (j - 1))][j - 1]); }
    }

     然后是查询,一开始我以为查询会像BIT那样一点一点逼近,这样的话效率也变成了O(lon(n))。

      比如

                                 [1,100]=[1,64]+[64,97]+[98,99]+[100,100]

    其中,长度为         

                                 100=64+32+2+1。

    但是可以同过交叉,把查询降到1

                                  [1,100]=[1,64]+[37,100]

    处理O(nlgn),查询O(1),很okey辣。

    int query(int L,int R)
    {
        int k=log2(R-L+1);
        return min(dp[L][k],dp[R-(1<<k)+1][k]);
    }

    ST

     

    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int maxn=1000010;
    int a[maxn],dp[maxn][20],n;
    void ST()
    {
        for(int j=1;j<=n;j++) dp[j][0]=a[j];
        for(int i=1;i<20;i++)
          for(int j=1;j<=n;j++){
               if(j+(1<<i)-1<=n)
               dp[j][i]=min(dp[j][i-1],dp[j+(1<<(i-1))][i-1]);
          }
    }
    int query(int L,int R)
    {
        int k=log2(R-L+1);
        return min(dp[L][k],dp[R-(1<<k)+1][k]);
    }
    int main()
    {
         int i,j,q,u,v;
         scanf("%d",&n);
         for(i=1;i<=n;i++) scanf("%d",&a[i]);
         ST();
         scanf("%d",&q);
         for(i=1;i<=q;i++){
                scanf("%d%d",&u,&v);
                printf("%d
    ",query(u,v));
         }
         return 0;
    }

    BIT

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    const int inf=100000000;
    const int maxn=1100000;
    int Min[maxn],a[maxn],n;
    void add(int u,int num)
    {
        while(u<=n){
            Min[u]=min(num,Min[u]);    
            u=u+(-u&u);
        }
    }
    void query(int L,int R)
    {
        int ans=inf;
        while(R>=L){
            //要用Min[R],必须满足R包括的范围>=L 
            while(R-(-R&R)>=L){//大范围比较 Min 
                ans=min(ans,Min[R]); 
                R=R-(-R&R);
            }
            //包括的范围超出L,则R-1. 
            if(R>=L) ans=min(ans,a[R]);//单点比较 a 
            R--;
        }
        printf("%d
    ",ans);
    }
    int main()
    {
        int i,j,q,u,v;
        scanf("%d",&n);
        for(i=1;i<=n;i++) Min[i]=inf;
        for(i=1;i<=n;i++) {
            scanf("%d",&a[i]);
            add(i,a[i]);
        }
        scanf("%d",&q);
        for(i=1;i<=q;i++) {
            scanf("%d%d",&u,&v);
            query(u,v);
        }
        return 0;
    }

     (这道题BIT的时间少于ST)

  • 相关阅读:
    NanoProfiler
    NanoProfiler
    Open Source Cassandra Gitbook for Developer
    Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar处理
    Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复
    Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常见错误
    Android Fragment使用(一) 基础篇 温故知新
    Set up Github Pages with Hexo, migrating from Jekyll
    EventBus源码解析 源码阅读记录
    Android M Permission 运行时权限 学习笔记
  • 原文地址:https://www.cnblogs.com/hua-dong/p/7786856.html
Copyright © 2011-2022 走看看