POJ 3264 Balanced Lineup (RMQ分析)

来源:互联网 发布:裂脑人 知乎 编辑:程序博客网 时间:2024/06/08 08:15

链接: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\n", maxsum - minsum);}}return 0;}


 

 

 

 

0 0