rmq+st
RMQ简单来说就是求区间的最大值(最小值)。
举个例子:
1 -2 9 10 15 38 -9
这里有 7 个数(随便输的),RMQ就是用来查询这些数中的最大值(最小值),但是是区间的。比如查询 [1,3] 这个区间的最大值 就是 9 这个值 .
RMQ一般有三种实现方式:暴力、线段树优化、st(dp),其中以st算法复杂度最低,预处理复杂度为o(nlogn),区间查询复杂度为o(1),
最小值(Mininun):我们可以用F(i,j)表示区间[i,i+2^j-1]间的最小值。我们可以开辟数组来保存F(i,j)的值,例如:F(2,4)就是保存区间[2,2+2^4-1]=[2,17]的最小值。那么F(i,0)的值是确定的,就为i这个位置所指的元素值,这时我们可以把区间[i,i+2^j-1]平均分为两个区间,因为j>=1的时候该区间的长度始终为偶数,可以分为区间[i,i+2^(j-1)-1]和区间[i+2^(j-1)-1,i+2^j-1],即取两个长度为2^(j-1)的块取代和更新长度为2^j的块,那么最小值就是这两个区间的最小值的最小值,动态规划为:F[i,j]=min(F[i,j-1],F[i+2^(j-1),j-1]).同理:最大值就是F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1]).
现在求出了F[i,j]之后又是怎样求出最大值或者最小值的,怎么转换为o(1)这种算法的~这就是ST算法:
这个时候询问时只要取k=ln(j-i+1)/ln2即可,那么可以令A为i到2^k的块,和B为到2^k结束的长度为2^k的块;那么A,B都是区间[i,j]的子区间,所以即求A区间的最小值和B区间的最小值的最小值。这个时候动态规划为:RMQ(i,j)=min(F[i,k],F[j-2^k+1,k]);
RMQ+st模板:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <set>
#include<malloc.h>
#include <map>
#include <vector>
#include <queue>
#define ri(n) scanf("%d",&n)
#define oi(n) printf("%d\n",n)
#define rl(n) scanf("%lld",&n)
#define ol(n) printf("%lld\n",n)
#define rep(i,l,r) for(i=l;i<=r;i++)
#define rep1(i,l,r) for(i=l;i<r;i++)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int epg=10-8;
int a[10000+10];
int dp[10000+10][30];
int n;
void solve()
{
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
dp[i][0]=a[i];
int len=(int)(log((double)n)/log(2.0));
//cout<<"len="<<len<<endl;
for(int j=1;j<=len;j++)
{
for(int i=0;i<n;i++)
{
if((i+(1<<j)-1)<n)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
//for(int i=0;i<n;i++)
//for(int j=0;j<n;j++)
//cout<<"dp["<<i<<"]["<<j<<"]="<<dp[i][j]<<endl;
}
int getmin(int i,int j)
{
int k=(int)(log((double)j-i+1)/log(2.0));
//cout<<"k="<<k<<endl;
return min(dp[i][k],dp[j-(1<<k)+1][k]);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
solve();
int x,y;
while(scanf("%d%d",&x,&y)!=EOF)
{
printf("%d\n",getmin(x,y));
}
return 0;
}