题意:给定一数组a[i],输入任意范围内的最大最小值之差。
题解:典型的RMQ(range min/max query)问题,对于此种问题,有三种较好的方法。
- 线段树:易知对于区间问题,可以使用区间树较好地解决。在建树时可以使用递归分治的方法,将[a,b]分成两段(a == b则直接返回对应数组值),在分治函数返回时,只需要比较左右两个子区间的最大最小值就能求出[a,b]之间的最大最小值。
- ST算法:动态规划的方法,以最小值为例
- 设dp[i][j] = min{a[k],i <= k <= i+2^j-1},即dp[i][j]为从dp[i]开始到第2^j个数为止的最小值,显然dp[i][0] = a[i]。
- 当j >= 1时,我们可以把上述规模为2^j的区间分成两部分,即[i,i+2^(j-1)-1]以及[i+2^(j-1),i+2^j-1],于是得到动态转移方程:dp[i][j] = min{dp[i][j-1],dp[i+2^(j-1)][j-1]}。
- 当查询区间[a,b]的最小值时我们同样可以把此区间分成两部分,假设分成2^n的两部分(可能有重叠),即有两区间[a,a+2^n-1]以及[b-2^n+1,b],易知要满足条件a + 2^n - 1 >= b - 2^n +1,得n >= log2(b-a+2)-1。
- 第三种方法较为复杂,可参考http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor#Range_Minimum_Query_%28RMQ%29
线段树:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define maxN 50005
#define maxM 20
int height[maxN];
class node
{
public:
int minHeight,maxHeight;
};
node tree[maxN*3];
class solve
{
private:
int N,Q;
int A,B;
int minHeight,maxHeight;
public:
solve(int n,int q):N(n),Q(q)
{
processIn();
buildTree(1,1,N);
while(Q--)
{
scanf("%d%d",&A,&B);
minHeight = 0X7FFFFFFF;
maxHeight = 0;
Search(1,1,N,A,B);
printf("%d\n",maxHeight-minHeight);
}
}
void processIn();
void buildTree(int nodeNo,unsigned short a,unsigned short b);
int Search(int nodeNo,unsigned short A,unsigned short B,unsigned short a,unsigned short b);
};
int solve::Search(int nodeNo,unsigned short A,unsigned short B,unsigned short a,unsigned short b)
{
if(A == a&&B == b)
{
minHeight = min(minHeight,tree[nodeNo].minHeight);
maxHeight = max(maxHeight,tree[nodeNo].maxHeight);
return 0;
}
int left = nodeNo<<1;
int right = left|1;
unsigned short mid = (A+B)>>1;
if(a > mid)
{
Search(right,mid+1,B,a,b);
}
else if(b <= mid)
{
Search(left,A,mid,a,b);
}
else
{
Search(left,A,mid,a,mid);
Search(right,mid+1,B,mid+1,b);
}
return 0;
}
void solve::buildTree(int nodeNo,unsigned short a,unsigned short b)
{
if(a == b)
{
tree[nodeNo].minHeight = tree[nodeNo].maxHeight = height[a];
return ;
}
int left = nodeNo<<1;
int right = left|1;
unsigned short mid = (a+b)>>1;
buildTree(left,a,mid);
buildTree(right,mid+1,b);
tree[nodeNo].minHeight = min(tree[left].minHeight,tree[right].minHeight); //取左右子树中最小值的较小值
tree[nodeNo].maxHeight = max(tree[left].maxHeight,tree[right].maxHeight); //取左右子树中最大值的较大值
return ;
}
void solve::processIn()
{
for(int i = 1;i <= N;i++)
{
scanf("%d",height+i);
}
return ;
}
int main()
{
int n,q;
while(~scanf("%d%d",&n,&q))
{
solve poj_3264(n,q);
}
return 0;
}
ST:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define maxN 50005
#define maxM 20
int height[maxN];
int dp_min[maxN][maxM];
int dp_max[maxN][maxM];
class solve
{
private:
int N,Q;
int A,B;
int max_j;
public:
solve(int n,int q):N(n),Q(q)
{
processIn();
RMQ_Init();
while(Q--)
{
A = strIn();
B = strIn();
printf("%d\n",RMQ(A,B));
}
}
void processIn();
void RMQ_Init();
int RMQ(int a,int b);
int strIn();
};
int solve::strIn()
{
int num = 0;
char c;
while((c = getchar())&&c != '\n'&&c != ' '&&c > 0)
{
num *= 10;
num += c-'0';
}
return num;
}
int solve::RMQ(int a,int b)
{
if(a == b)
return 0;
int k = ceil(log((double)(b-a+2))/log(2.0))-1;
int tmpMin = min(dp_min[a][k],dp_min[b-(1<<k)+1][k]);
int tmpMax = max(dp_max[a][k],dp_max[b-(1<<k)+1][k]);
return tmpMax-tmpMin;
}
void solve::RMQ_Init()
{
int i,j;
max_j = floor(log((double)(N+1))/log(2.0));
for(i = 1;i <= N;i++)
{
dp_min[i][0] = dp_max[i][0] = height[i];
}
for(j = 1;j <= max_j;j++)
{
for(i = 1;i+(1<<(j-1)) <= N;i++)
{
dp_min[i][j] = min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
dp_max[i][j] = max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
}
}
return ;
}
void solve::processIn()
{
getchar();
for(int i = 1;i <= N;i++)
{
height[i] = strIn();
}
return ;
}
int main()
{
int n,q;
while(~scanf("%d%d",&n,&q))
{
solve poj_3264(n,q);
}
return 0;
}