題意:給定一數組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;
}