今天学弟在群里直播讲课,讲了RMQ,以前摸鱼太多这个题目并没看出来是啥,然后就去凑了个热闹。

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

对于这个问题,区间最值,多次查询,我第一反应是线段树,也确实是线段树最值问题的基础模型,线段树博客其他文章中有,这里不单独拿出来了。

学弟讲这个题目,讲了3中方法暴力、动态规划,ST(Sparse Table)

暴力:没什么好说

int RMQ(int A[], int mi, int ma) {
	int rt_max = -INF, rt_min = INF;
	for(int i=mi; i<=ma; i++) {
		rt_max = max(A[i], rt_max);
		rt_min = min(A[i], rt_min);
	}
	return {...};
}

动态规划:

dp[i][j]维护为从i到j的最大值或最小值,状态转移方程即:

dp[i][j]=max(dp[i][j-1],dp[j][j]);

这样在最开始将所有的值全部进行预处理,每次查询只要查询dp[i][j]的值,就能直接得到A[i]-A[j]的最大值和最小值:

int dp[MX][MX];
void init_RMQ(int A[], int n) {
	for(int i = 0; i < n; i++) {
		dp[i][i] = A[i];
	}
	for(int i = 0; i < n; i++) {
		for(int j = i + 1; j < n; j++) {
			dp[i][j] = max(dp[i][j-1], dp[j][j]);
		}
	}
}

ST算法:

这是一种用二分优化的DP,dp[i][j]为从i开始到i+2^j-1的最值

dp[i][0] = max(A[i] to A[i + (2^j) - 1]) ...A[i]			len = 2^0 = 1 
dp[i][1] = max(A[i] to A[i + (2^j) - 1]) ...A[i],A[i+1];   		len = 2^1 = 2
dp[i][2] = max(A[i] to A[i + (2^j) - 1]) ...A[i],A[i+1],A[i+2],A[i+3]  	len = 2^2 = 4
dp[i][3] = .... len = 2^3 = 8 ...

 

dp[i][j]的状态转移方程如下

dp[i][j] = max(dp[i][j - 1], dp[i + 2^(j-1)][j - 1]);
void init_RMQ(int A[], int n) {
	for(int i = 1; i <= n; i++) {
		dp[i][0] = A[i];
	}
	for(int j = 1; (1<<j) <= n; j++) {
		for(int i = 1; (i + (1<<j) - 1) <= n; i++) {
			dp[i][j] = max(dp[i][j-1], dp[i + (1<<(j-1))][j-1]);
		}
	}
}

当我们需要查询A[1]-A[7]的时候,查询的长度为7,我们则可以查询两个长度为4的区间来确定这个区间的最值,而log2(4) = 2

转化成dp则为只需要查询dp[1][2]和dp[7-4+1][2]的两个的最值比较即可:max(dp[1][2],dp[4][2])

int Query_max(int l,int r) {
	int k= log2(r - l + 1);
	return ( max(dp[l][k], dp[r - (1<<k) + 1][k]));
}

  

分类:

技术点:

相关文章: