RMQ问题是区间求最值问题,就是求一个数组第i个到第j个中最大数或最小数的算法。
这个算法有一些倍增思想,也有一些二分思想。具体是一个数组,m[i][j]表示从i开始往后数2的j次方个数的最大值或最小值是几。
我们可以很明显的看出,m[i][j]=max(m[i][j-1],m[i+pow(2,j-1)][j-1]);这样就能求出所有i后2的j次方里最大的数了。
但是有一个问题,(可能只有我不会。)如果让查找的区间长度不是2的次方怎么办?我也是想了20分钟。得到了结果,让中间重合一部分,凑成2个log(r-l+1)次方。就可以了。
具体操作是max(l开始向后查看2的log(r-l+1)次方,r向前查看2的log(r-l+1)次方),或者min。这样每次运行都是2的次方,也没有多查找。
有一个地方大家需要注意,要提前算,要提前算,要提前算!!!
提前算好特别香,中间pow慢到死哎。
正经代码来了:
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cmath> #include<cstring> using namespace std; long long m[100005][20],md[100005][20],l,r,n,mid,d,x,s,sz[30]={1}; int main() { cin>>n>>s; for(int i=1;i<=20;i++) { sz[i]=sz[i-1]*2; } for(int i=1;i<=n;i++) { scanf("%lld",&md[i][0]); } for(int j=1;j<=log2(n);j++) { for(int i=1;i+sz[j]-1<=n;i++) { md[i][j]=max(md[i][j-1],md[i+sz[j-1]][j-1]); } } for(int i=0;i<s;i++) { scanf("%lld%lld",&l,&r); mid=log2(r-l+1); d=max(md[l][mid],md[r-sz[mid]+1][mid]); printf("%lld ",d); } return 0; }
这个代码对应洛谷P3865。
应该是对的,大家懂了吗,自己去试试吧。
对了,从今天开始我要把某谷春令营的作业以及算法仔细学习,然后用自己认为简单的方式来讲解,希望可以帮助到去比赛的同学。