题目链接http://acm.nyist.net/JudgeOnline/problem.php?pid=119
大致题意是求区间最值问题,若采取常规方法,当数据数量大时,时间复杂度为O(n^2),不是一个好的方法。
下面介绍一种RMQ算法,RMQ故名思意 Range Minsum/Maxsum Query,求区间的最大与最小值
RMQ是一种DP的思想。
Step 1.预处理,过程耗时O(nlogn)
设A[i]是要求区间最值的数列,不妨设maxsum[i][j],表示从A[i]起的连续2^j个数的最大值,同理minsum。
如maxsum[1][0],2^0=1,即maxsum[i][0]=A[i];
maxsum[1][3]即为区间 A[1]....A[8]的最大值
DP就得有状态转移方程
这里运用了二分的思想
有状态方程
maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+2^j-1][j-1]);
同理 minsum[i][j]=min(minsum[i][j-1],minsum[i+2^j-1][j-1]);
Step 2
查询区间最值
同样有个方程
maxsum[x][y]=max(maxsum[x][k],maxsum[y-(1<<k)+1][k]; 其中k为log(y-x+1.0)/log(2.0)
举例maxsum[2][8]=max(max[2][2],maxsum[5][2]) maxsum[2][2]=max(A[2],A[3],A[4],A[5]);
maxsum[5][2]=max(A[5],A[6],A[7],A[8]);
下面来看这道题,注意+的运算优先级大于<<。
AC代码:
#include<stdio.h>
#include<iostream>
#include<cmath>
using namespace std;
#define N 1000010
int maxsum[N][20],minsum[N][20];
void RMQ(int num)
{
int i,j;
for(j=1;j<20;j++)
for(i=1;i<=num;i++)
{
if(i+(1<<j)-1<=num)
{
maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+(1<<j-1)][j-1]);
minsum[i][j]=min(minsum[i][j-1],minsum[i+(1<<j-1)][j-1]);
}
}
}
int main()
{
int num,q;
scanf("%d%d",&num,&q);
for(int i=1;i<=num;++i)
{
scanf("%d",&maxsum[i][0]);
minsum[i][0]=maxsum[i][0];
}
RMQ(num);
while(q--)
{
int x,y,k;
scanf("%d%d",&x,&y);
k=int(log(y-x+1.0)/log(2.0));
maxsum[x][y]=max(maxsum[x][k],maxsum[y-(1<<k)+1][k]);
minsum[x][y]=min(minsum[x][k],minsum[y-(1<<k)+1][k]);
printf("%d\n",maxsum[x][y]-minsum[x][y]);
}
}