C++ RMQ问题

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。

应该是对的,大家懂了吗,自己去试试吧。

对了,从今天开始我要把某谷春令营的作业以及算法仔细学习,然后用自己认为简单的方式来讲解,希望可以帮助到去比赛的同学。

原文地址:https://www.cnblogs.com/lichangjian/p/12418914.html