题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2176
范围最小值问题(Range Minimum Query,RMQ).实践中最常用的是Sparse Table 算法,预处理时间是O(nlogn),查询只需要O(1),而且常数很小。
注意到整个数组是非降序的,所有相等元素会聚集到一起。这样可以把整个数组进行游程编码。比如-1,1,1,2,,2,2,4可以编码成(-1,1)(1,2),(2,3),(4,1),其中(a,b)表示有b个连续的a.
#include <cstdio>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=100000+5;
int a[maxn],l[maxn],r[maxn],num[maxn];
///num[p]表示位置p所在段的编号
///l[p],r[p]分别表示所在段左右端点的位置
int d[maxn][25];
//vector<int> count;
void RMQ_init(const vector<int>& A)
{
int v=A.size();
for(int i=1;i<v;i++)
d[i][0]=A[i];
for(int j=1;(1<<j)<=v;j++)
for(int i=0;i+(1<<j)-1<v;i++)
d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R)
{
int k=0;
while((1<<(k+1))<=R-L+1) k++;///如果2^(k+1)<=R-L+1,那么k还可以加1
return max(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
int q,n;
while(~scanf("%d",&n)&&n!=0){
scanf("%d",&q);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
a[n]=a[n-1]+1;
int start=-1;
vector<int> count;
for(int i=0;i<=n;i++)
if(i==0||a[i]>a[i-1]){
if(i>0){
count.push_back(i-start);
for(int j=start;j<i;j++){
num[j]=count.size()-1,l[j]=start,r[j]=i-1;
}
}
start=i;
}
RMQ_init(count);
while(q--){
int L,R,ans;
scanf("%d%d",&L,&R);
L--;R--;
if(num[L]==num[R]) ans=R-L+1;
else{
///最后结果是从L到L所在段结束的元素个数,
///从R所在段开始处到R处元素个数,
///中间第num[L]+1段到第num[R]-1段的count的最大值
ans=max(r[L]-L+1,R-l[R]+1);
if(num[L]+1<=num[R]-1) ans=max(ans,RMQ(num[L]+1,num[R]-1));
}
printf("%d\n",ans);
}
}
return 0;
}
本文介绍了一种解决范围最小值问题的有效方法——SparseTable算法,并结合游程编码技术来优化处理非降序数组。通过C++实现,展示了如何初始化和查询数据,适用于竞赛编程及实际应用。
(训练指南)&spm=1001.2101.3001.5002&articleId=8818770&d=1&t=3&u=db261b4362ee418f800871b2a1dfb2bb)
7万+

被折叠的 条评论
为什么被折叠?



