zoukankan      html  css  js  c++  java
  • 关于RMQ问题

    所谓RMQ问题,就是求区间最值的问题

    这里先给出一道例题 : 

    题目描述

    老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。

    输入输出格式

    输入格式:

    输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。

    第二行为m个数,分别是账目的钱数

    后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。

    输出格式:

    输出文件中为每个问题的答案。具体查看样例。

    输入输出样例

    输入样例#1:
    10 3
    1 2 3 4 5 6 7 8 9 10
    2 7
    3 9
    1 10
    输出样例#1:
    2 3 1

    sol :

    首先,我们可以想到用线段树去维护区间最值,那么这就是道模板题

    但是,其实还有一个专门为RMQ问题设计的算法:ST算法

    ST算法的核心其实是dp(也用到了二分的思想)

    假设我们维护的是区间最小值,我们用数组minn[i][j]表示以开头之后连续2^j个数之中的最小值
    那么初始化过程就是 minn[i][0] = a[i] (a[]为原数组)
    我们可以推出状态转移方程 minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]); (这里用到了位运算,1<<j表示的就是2^j)
    那么具体的转移过程就可以写出来了 :
    for(int j=1;1<<j<=m;j++) (想一下为什么是j循环放在外面?)
    for(int i=1;i+(1<<j)-1<=m;i++)
    minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
    (其实思路就是把两个长度为2^(j-1)的区间合并成一个长度为2^j的区间,所以第一层循环当然是j啦=-=)

    最后再讲一下查询的问题
    假设我们要查找的区间的长度不是2^x,比如我们要查找区间[2,6]的最小值,那么该怎么办呢?
    我们观察到要查询的区间长度为5,而我们维护了长度为2^x的区间所以我们可以查询区间[2,5]和区间[3,6]的最小值,然后取小就可以了

    综上,ST算法构造区间最值时的时间复杂度为O(nlogn),单次查询的复杂度为O(1)

    下面附例题代码:(ST算法)
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    
    int m,n;
    int minn[100001][22] ;
    
    void RMQ_init()
    {
        for(int j=1;(1<<j)<=m;j++)
          for(int i=1;i+(1<<j)-1<=m;i++)
            minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
    }
    
    int RMQ_ask(int l,int r)
    {
        int k = log2(r-l+1) ;
        return min(minn[l][k],minn[r-(1<<k)+1][k]) ;
    }
    
    int main()
    {
        cin>>m>>n;
        for(int i=1;i<=m;i++) scanf("%d",&minn[i][0]) ;
        RMQ_init() ;
        int x,y ;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&x,&y) ;
            printf("%d ",RMQ_ask(x,y)) ;
        }
        return 0;
    }

    ——end;

     
  • 相关阅读:
    C#枚举的key值获取枚举name名称OK
    《[C#] int与System.Int32有什么区别》
    《[C#] int与System.Int32有什么区别》
    《算法竞赛入门经典》习题43 黑白棋(Othello, ACM、ICPC World Finals 1992, UVa220)
    《算法竞赛入门经典》习题42 正方形 (Squares,ACM,ICPC World Finals 1990,UVa201)——仅提供大体方法
    《算法竞赛入门经典》习题42 正方形 (Squares,ACM,ICPC World Finals 1990,UVa201)——仅提供大体方法
    《算法竞赛入门经典》习题42 正方形 (Squares,ACM,ICPC World Finals 1990,UVa201)——仅提供大体方法
    百练4103:踩方格
    百练4103:踩方格
    百练4103:踩方格
  • 原文地址:https://www.cnblogs.com/zub23333/p/7722197.html
Copyright © 2011-2022 走看看