POJ 3264 Balanced Lineup (RMQ分析)

链接:http://poj.org/problem?id=3264

RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。

动态规划:

设data[i]是要求最大值的数列。dp[i][j]表示以第i个数开始,长度为2^j段的最大值。 若将该段从中间分开(因为是2^j,肯定能分成两段),得到的子序列为 i~2^(j-1)-1 i+2^(j-1)~i+2^j-1。。写成方程的形式,即为:

dp[i][j]=strcat( dp[i][j-1] , dp[i+2^[j-1]][j-1] )  //strcat()为字符串连接函数

So....状态转移方程为:dp[i][j]=MAX( dp[i][j-1] , dp[i+2^[j-1]][j-1] ) ;

dp[i][0]为它本身

以下摘自:http://blog.csdn.net/niushuai666/article/details/7401403

代码为:

void RMQ(int num) //预处理->O(nlogn)
{
	for(int j = 1; j < 20; ++j)
		for(int 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]);
			}
}

其中1<<j为2^j

 

这里我们需要注意的是循环的顺序,我们发现外层是j,内层所i,这是为什么呢?可以是i在外,j在内吗?


答案是不可以。因为我们需要理解这个状态转移方程的意义。

状态转移方程的含义是:先更新所有长度为F[i,0]即1个元素,然后通过2个1个元素的最值,获得所有长度为F[i,1]即2个元素的最值,然后再通过2个2个元素的最值,获得所有长度为F[i,2]即4个元素的最值,以此类推更新所有长度的最值。

而如果是i在外,j在内的话,我们更新的顺序就是F[1,0],F[1,1],F[1,2],F[1,3],表示更新从1开始1个元素,2个元素,4个元素,8个元素(A[0],A[1],....A[7])的最值,这里F[1,3] = max(max(A[0],A[1],A[2],A[3]),max(A[4],A[5],A[6],A[7]))的值,但是我们根本没有计算max(A[0],A[1],A[2],A[3])和max(A[4],A[5],A[6],A[7]),所以这样的方法肯定是错误的。


为了避免这样的错误,一定要好好理解这个状态转移方程所代表的含义。



(二)然后是查询。

假如我们需要查询的区间为(i,j),那么我们需要找到覆盖这个闭区间(左边界取i,右边界取j)的最小幂(可以重复,比如查询5,6,7,8,9,我们可以查询5678和6789)。

因为这个区间的长度为j - i + 1,所以我们可以取k=log2( j - i + 1),则有:RMQ(A, i, j)=max{F[i , k], F[ j - 2 ^ k + 1, k]}。

举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2]);


在这里我们也需要注意一个地方,就是<<运算符和+-运算符的优先级。

比如这个表达式:5 - 1 << 2是多少?


答案是:4 * 2 * 2 = 16。所以我们要写成5 - (1 << 2)才是5-1 * 2 * 2 = 1。

 

 

 

代码:

#include<iostream>
#include<cstdio>

using namespace std;

const int N = 50005;
int dpmax[N][20], dpmin[N][20];

void RMQ(int n)
{
	for(int j = 1; j != 20; j++)
		for(int i = 1; i <= n; i++)	
			if(i + (1 << j) - 1 <= n)					//i+2^j
			{
				dpmax[i][j] = max(dpmax[i][j - 1], dpmax[i + (1 << (j - 1))][j - 1]);			// i+2^(j-1)
				dpmin[i][j] = min(dpmin[i][j - 1], dpmin[i + (1 << (j - 1))][j - 1]);
			}
		
	
}

int main()
{
	int num, query;
	int a, b;
	while(scanf("%d %d", &num, &query) != EOF)
	{
		for(int i = 1; i <= num; ++i)
		{
			scanf("%d", &dpmax[i][0]);
			dpmin[i][0] = dpmax[i][0];
		}
		RMQ(num);
		while(query--)
		{
			scanf("%d%d", &a, &b);
			int k = (int)(log(b - a + 1.0) / log(2.0));				//log2(b-a+1)....换底公式
			int maxsum = max(dpmax[a][k], dpmax[b - (1 << k) + 1][k]);
			int minsum = min(dpmin[a][k], dpmin[b - (1 << k) + 1][k]);
			printf("%d
", maxsum - minsum);
		}
	}
	return 0;
}


 

 

 

原文地址:https://www.cnblogs.com/frankM/p/4399526.html