区间最值(RMQ)的几种方法(知识整理+板子总结)(单调队列/multiset/ST表/线段树)

板子

以poj3264为例,mx[i][j]代表下标从i开始的连续区间长度为2^{j}的最大值,mn[i][j]为最小值

ST表

​#include<iostream>
using namespace std;
const int N=1e6+10,M=22;
int n,q,l,r,v;
int mx[N][M],mn[N][M],lg[N];
void init(){
    for(int i=2;i<N;++i){
        lg[i]=lg[i>>1]+1;
    }
}
void ST(int n){
	for(int len=1;(1<<len)<=n;++len){
		for(int i=1;i+(1<<len)-1<=n;++i){
			mx[i][len]=max(mx[i][len-1],mx[i+(1<<(len-1))][len-1]);
			mn[i][len]=min(mn[i][len-1],mn[i+(1<<(len-1))][len-1]);
		}
	}
}
int RMQ(int l,int r,int op){
	int k=lg[r-l+1];
	int ans;
	if(!op)ans=max(mx[l][k],mx[r-(1<<k)+1][k]);
	else ans=min(mn[l][k],mn[r-(1<<k)+1][k]);
	return ans;
}
int main(){
    init();
	while(~scanf("%d%d",&n,&q)){
		for(int i=1;i<=n;++i){
			scanf("%d",&v);
			mx[i][0]=mn[i][0]=v;
		}
		ST(n);
		for(int i=1;i<=q;++i){
			scanf("%d%d",&l,&r);
			printf("%d\n",RMQ(l,r,0)-RMQ(l,r,1));
		}
	}
	return 0;
}

​

总结

个人觉得博客的这个粘代码背景没我的黑底白字好看QAQ

总结一波,代码总是越敲越熟的嘛……知识沉积的过程……

到时候改一改,传参的时候把数组名一起传进去,就能当小黑盒用了

法一(单调队列,O(n)):

//区间连续k个的最小 传进去下标 
    deque<int>q;
    for(int i=1;i<k;++i)
    {
    	while(!q.empty()&&high[q.back()]>high[i])q.pop_back();
    	q.push_back(i); 
    }
    for(int i=k;i<n;++i)
	{
	    while(!q.empty()&&high[q.back()]>high[i])q.pop_back();
    	    q.push_back(i); 
		ans=max(ans,high[q.front()]);
		if(q.front()<=i-k+1)q.pop_front();
	} 

法二(multiset,O(nlogn)):

这个东西和set其实没啥区别,

允许数字被重复插入,操作差不太多

//区间连续k个的最小 传进去值 
	multiset<int>q; 
	for(int i=1;i<k;++i)q.insert(high[i]);
        for(int i=k;i<n;++i)
	{
		q.insert(high[i]);
		ans=max(ans,*q.begin());
		q.erase(q.find(high[i-k+1]));
	} 

法三(ST表RMQ,预处理O(nlogn),查询O(1)):

void ST(int n)
{
	for(int i=1;i<n;++i)dp[i][0]=high[i];
	for(int j=1;(1<<j)<n;++j)
	{
		for(int i=1;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=log(r-l+1)/log(2);
	return min(dp[l][k],dp[r-(1<<k)+1][k]); 
}
ST(n); 
for(int i=1;i+k-1<n;++i)
ans=max(ans,RMQ(i,i+k-1));

法四(线段树,预处理O(nlogn),查询O(logn))

挺好写的吧,基础操作……

有前面那么优秀的几种就不写这个了QAQ

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值