【动态规划dp】RMQ问题(st算法)

【RMQ】 Range Minimum/Maximum Query  范围最值问题

【ST算法】

  解决RMQ问题的有效算法

  

  预处理   经过预处理构造出d,预处理时间复杂度 O(nlogn)

  运用动态规划的思想   d(i, j) 表示 范围 i ~ i + 2j-1 的最小值则有状态转移方程

  d(i, j) = min {      d(i, j-1)       ,    d(i + 2j-1 , j-1)     }

  

  设原数据存储在数组a[]  , 则初始状态

  d( i ,  0 ) = a[ i ]

  查询给定区间的最值 时间复杂度O(1)

  设查询区间为 [ L, R ]

  区间长度为 length = R - L + 1  < =   2k (其中k为满足条件的最大整数) 

  则查询结果为 min {  d(L, k)   ,   d( r- 2k + 1 ,  k )   }

  

【模板】

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

/*
RMQ-ST算法
区间最小值模板
区间最大值需更改 min() 为 max()
by chsobin
*/
const int maxn = 100;
int dp[maxn][maxn];
void RMQ_init(const vector<int>& A){
    int n = A.size();
    for(int i=0;i<n;++i) dp[i][0] = A[i];
    for(int j=1; (1<<j)<=n; j++){
        for(int i=0; i+(1<<j)-1<n; i++){
            dp[i][j] = min(dp[i][j-1], dp[i + (1<<(j-1))][j-1]);
        }
    }
}
int RMQ(int L, int R){
    int k = 0;
    while( (1<<(k+1)) <= R-L+1 ) k++;
    return min(dp[L][k], dp[R-(1<<k)+1][k]);
}

int main()
{
    int a[10] = {1, 5, 8, 7, 2, 6, 1, 9, 3, 4};
    vector<int> A(a, a+10);    

    RMQ_init(A);
    for(int i=0;i<A.size();++i){
        cout << A[i] << " ";
    }
    cout << endl;
    while(1){
        int L, R;
        cout << "[L, R]";
        cin >> L >> R;
        cout << RMQ(L, R) << endl;
    }
    return 0;
} 

【题目】

  hdu3183

/*
解题思路:
使用RMQ,设原数字长为n,那么除去m个数字后还剩n-m个数字。
(1)因为有n-m个数字,那么在1到m+1位置中最小的那个数字必是结果中的第一个数字,记录其位置为pos
(2)然后从这个位置的下个位置pos+1开始到m+2位置的数字中最小的那个数字必定是结果中第二个数字,以此类推下去向后找。
(3)为了保证数字最小,所以要保证高位最小,还要保证数字长度满足条件
*/ 

#include<iostream>
#include<cstring>
using namespace std;

const int maxn = 1010;
char a[maxn];
int dp[maxn][11];
char ans[maxn];
int n, m;

//自定义 比较规则
int MIN(int i, int j){
    return a[i] <= a[j] ? i : j;
}

//预处理
void init(){
    for(int i=0;i<n;++i){
        dp[i][0] = i;
    }
    for(int j=1;(1<<j)<=n;++j){
        for(int i=0;(i + (1<<j) -1 ) < n;++i){
            dp[i][j] = MIN(dp[i][j-1], dp[i+ (1<<(j-1)) ][j-1]);
        }
    }
}

//查询
int query(int L, int R){
    int k=0;
    int temp = R-L+1;
    int sum = 1;
    while(sum <= temp){
        ++k;
        sum *= 2;
    }
    k--;
    return MIN( dp[L][k], dp[R-(1<<k)+1][k] );
}

int main(){
    int L = 0;
    int R = 0;
    while(scanf("%s%d", a, &m)==2){
        int index = 0;
        L = 0;
        R = m;
        n = strlen(a);
        m = n - m;
        init();

        //
//        for(int i=0;i<n;++i){
//            for(int j=0; (i + (1<<j) -1 ) < n; ++j){
//                printf("%c ", a[dp[i][j]]);
//            }
//            printf("\n");
//        }

        while(m--){
//            cout << "L = "<< L << "R = " << R << endl;
            L = query(L, R);
            ans[index++] = a[L];
            L++;
            R++;
            if(R==n) R = n-1;        //保证R不越界
        }
        ans[index] = ‘\0‘;
        int k = 0;
        for(;k<index;++k){            //去除前导零
            if(ans[k] != ‘0‘) break;
        }
        if(k==index) printf("0\n");
        else printf("%s\n", ans+k);
    }
    return 0;
} 

AC 0ms

【参考】

  白书P197

原文地址:https://www.cnblogs.com/chcaxi/p/8338491.html

时间: 2024-10-15 08:27:46

【动态规划dp】RMQ问题(st算法)的相关文章

RMQ的ST算法

·RMQ的ST算法    状态设计:        F[i, j]表示从第i个数起连续2^j个数中的最大值    状态转移方程(二进制思想):        F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])    查询时:        因为这个区间的长度为j - i + 1,所以我们可以取k=log2( j - i + 1),        则有:RMQ(A, i, j)=max{F[i , k], F[ j - 2 ^ k + 1, k]}.

RMQ问题ST算法

1. 概述 RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值.这两个问题是在实际应用中经常遇到的问题,下面介绍一下解决这两种问题的比较高效的算法.当然,该问题也可以用线段树(也叫区间树)解决,算法复杂度为:O(N)~O(logN),这里我们暂不介绍. 2.RMQ算法 对于该问题,最容易想到的解决方案是遍历,复杂度是O(n).但当数据量

RMQ问题——ST算法

什么是RMQ.ST:RMQ(Range Minimum/Maximum Query)问题,即求区间的最值.可以写一个线段树来实现,但是每次查询的时间复杂度为O(log n),若查询次数过多则可能超时.ST算法是一种离线算法,经过O(nlogn)的预处理后,可以在O(1)的时间复杂度内进行查询,缺点是无法对数据做出修改. 算法实现: 初始化:用dp实现初始化.a[]为原始数据数组f,[i][j]表示从i向后的2j个数字中的最值.显然f[i][0]=a[i]; 我们将f[i][j]分为两段,一段为a

RMQ问题 ST算法

RMQ是询问某个区间的最大值或最小值的问题,主要求解方法之一ST算法: ST算法其实是倍增思想的产物,等下看代码实现就很明显了 ST算法通常用在要多次询问一些区间的最值的问题中,相比于线段树,它的程序实现更简单,运行速度更快; ST算法没有修改操作(或者说不擅长动态修改) ST算法流程: 预处理:ST算法的原理实际上是动态规划,我们用a数组表示一组数,设\(f[i,j]\)表示从\(a[i]\)到\(a[i+2^j-1]\)这个范围内的最大值,从中间平均分成两部分,即把\(f[i,j]\)分为\

RMQ(ST算法)

RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列a,回答若干询问RMQ(A,i,j)(i, j<=n),返回数列a中下标在i,j之间的最小/大值.如果只有一次询问,那样只有一遍for就可以搞定,但是如果有许多次询问就无法在很快的时间处理出来.在这里介绍一个在线算法.所谓在线算法,是指用户每输入一个查询便马上处理一个查询.该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询.ST(Sparse Table

[总结]RMQ问题&amp;ST算法

目录 一.ST算法 二.ST算法の具体实现 1. 初始化 2. 求出ST表 3. 询问 三.例题 例1:P3865 [模板]ST表 例2:P2880 [USACO07JAN]平衡的阵容Balanced Lineup 一.ST算法 ST算法(Sparse Table Algorithm)是用于解决RMQ问题(区间最值问题,即Range Maximum/Minimum Question)的一种著名算法. ST算法能在复杂度为\(O(NlogN)\)的预处理后,以\(O(1)\)的复杂度在线处理序列区

RMQ问题ST算法 (还需要进一步完善)

/* RMQ(Range Minimum/Maximum Query)问题: RMQ问题是求给定区间中的最值问题.当然,最简单的算法是O(n)的,但是对于查询次数很多(设置多大100万次),O(n)的算法效率不够.可以用线段树将算法优化到O(logn)(在线段树中保存线段的最值).不过,Sparse_Table算法才是最好的:它可以在O(nlogn)的预处理以后实现O(1)的查询效率.下面把Sparse Table算法分成预处理和查询两部分来说明(以求最小值为例). 预处理: 预处理使用DP的思

RMQ之ST算法模板

1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 using namespace std; 5 const int N=1e6+111; 6 int Max[N][21],Min[N][21],a[N]; 7 void ST(int *a,int n)//预处理,O(NlogN) 8 { 9 for(int i=1;i<=n;i++) 10 Min[i][0]=Max[i][0]=a[i

RMQ之ST算法

1 #include <stdio.h> 2 #include <string.h> 3 const int N = 100; 4 int a[N]; 5 int dp[N][33]; 6 inline int min(const int &a, const int &b) 7 { 8 return a < b ? a : b; 9 } 10 11 /* 12 dp[i][j] 表示以i开头的,长度为2^j的区间中的最小值 13 很明显dp[i][0] = a

RMQ问题与ST算法

RMQ(Range Minimum/Maximum Query)问题是求区间最值问题. 对于长度为 n 的数组 A,进行若干次查询,对于区间 [L,R] 返回数组A中下标在 [L,R] 中的最小(大)值. 可以用线段树来解决这个问题,预处理的复杂度是 O(nlogn),查询的复杂度是 O(logn). 更好的解法是ST算法.Sparse_Table算法,即稀疏表算法,这个方法可以在 O(nlogn) 的预处理后达到 O(1) 的查询代价. 这个算法非常容易实现. 定义 F[ i, k ] 表示从