动态规划求区间最值问题RMQ(Range Minimum/Maximum Query)
2012-12-03 20:56
591 查看
求区间最值问题最简单的方法是遍历区间,时间复杂度O(n), 但是当查询次数很多的时候,时间效率并不高。可以用线段树把算法优化到O(logn) (在线段树中保存线段的最值),不过sparse_table算法是较好的,用O(nlogn)的时间进行预处理,然后用O(1)时间进行查询。
预处理:
预处理使用DP的思想,f(i, j)表示[i, i+2^j - 1]区间中的最小值,我们可以开辟一个数组专门来保存f(i, j)的值。例如,f(0, 0)表示[0,0]之间的最小值,就是num[0], f(0, 2)表示[0, 3]之间的最小值, f(2, 4)表示[2, 17]之间的最小值注意, 因为f(i, j)可以由f(i, j - 1)和f(i+2^(j-1), j-1)导出, 而递推的初值(所有的f(i, 0) = i)都是已知的,所以我们可以采用自底向上的算法递推地给出所有符合条件的f(i, j)的值。
查询:
假设要查询从m到n这一段的最小值, 那么我们先求出一个最大的k, 使得k满足2^k <= (n - m + 1).于是我们就可以把[m, n]分成两个(部分重叠的)长度为2^k的区间: [m, m+2^k-1], [n-2^k+1, n];而我们之前已经求出了f(m, k)为[m, m+2^k-1]的最小值, f(n-2^k+1, k)为[n-2^k+1, n]的最小值,我们只要返回其中更小的那个, 就是我们想要的答案, 这个算法的时间复杂度是O(1)的.
例如, rmq(0, 11) = min(f(0, 3), f(4, 3))
注意:这里空间复杂度为 O(n * n),可以采用动态数组的方法,只用两列.
预处理:
预处理使用DP的思想,f(i, j)表示[i, i+2^j - 1]区间中的最小值,我们可以开辟一个数组专门来保存f(i, j)的值。例如,f(0, 0)表示[0,0]之间的最小值,就是num[0], f(0, 2)表示[0, 3]之间的最小值, f(2, 4)表示[2, 17]之间的最小值注意, 因为f(i, j)可以由f(i, j - 1)和f(i+2^(j-1), j-1)导出, 而递推的初值(所有的f(i, 0) = i)都是已知的,所以我们可以采用自底向上的算法递推地给出所有符合条件的f(i, j)的值。
查询:
假设要查询从m到n这一段的最小值, 那么我们先求出一个最大的k, 使得k满足2^k <= (n - m + 1).于是我们就可以把[m, n]分成两个(部分重叠的)长度为2^k的区间: [m, m+2^k-1], [n-2^k+1, n];而我们之前已经求出了f(m, k)为[m, m+2^k-1]的最小值, f(n-2^k+1, k)为[n-2^k+1, n]的最小值,我们只要返回其中更小的那个, 就是我们想要的答案, 这个算法的时间复杂度是O(1)的.
例如, rmq(0, 11) = min(f(0, 3), f(4, 3))
注意:这里空间复杂度为 O(n * n),可以采用动态数组的方法,只用两列.
#include <stdio.h> #include <string.h> #include <math.h> #define max(a,b) ((a)>(b)?(a):(b)) int map[100][100]; /** * 花费O(nlogn)的时间进行预处理 递推公式 * f[i,j]=max(f[i, j-1], f[i + 2^(j-1), j-1]) * @param m 存储数值的数组 * @param n 数组长度 */ void pre_handle(int * m , int n) { memset(map, 0, sizeof(map)); for(int i=0;i<n;i++) map[i][0]=m[i]; int k = (int)(log(n) / log(2)); for(int i=1;i<=k;i++) /* i表示列,j表示行, 每一列的结果只依赖于前一列 */ for(int j = 0; j + pow(2,i-1) < n; j++) /* 注意因为每列都限制j + pow(2,i-1) < n,而除了第一列以外 * 其它都不到n-1,所有每一行的最后一个数有可能是不对的,但 * 是不影响最后结果 */ map[j][i]=max(map[j][i-1], map[j+(int)pow(2, i-1)][i-1]); } int RMQ(int a, int b) { int k = (int)(log(b-a+1)/log(2)); /* 区间长度为b-a+1 */ /* 两个区间之间有重合 */ return max(map[a][k], map[b+1-(int)pow(2, k)][k]); } int main() { freopen("in", "r", stdin); int n; int m[100]; scanf("%d", &n); for(int i=0;i<n;i++) scanf("%d", &m[i]); pre_handle(m, n); printf("%d\n", RMQ(2, 10)); printf("%d\n", RMQ(20, 30)); return 0; }
相关文章推荐
- 动态规划求RMQ(区间最值问题Range Minimum/Maximum Query)
- 动态规划求RMQ(区间最值问题Range Minimum/Maximum Query)
- RMQ (Range Minimum/Maximum Query)问题
- RMQ (Range Minimum/Maximum Query)算法(查询区间最值)
- RMQ (Range Minimum/Maximum Query)问题的ST(Sparse Table)解法
- RMQ (Range Minimum/Maximum Query)区间最值查询
- RMQ(Range Minimum/Maximum Query)问题:
- RMQ (Range Minimum/Maximum Query)问题
- 动态规划法求解RMQ(range minimum/maximum query)问题
- [数据结构-查询区间最小值小结(RMQ问题(Range Minimum Query))]
- [数据结构-查询区间最小值小结(RMQ问题(Range Minimum Query))]
- RMQ(Range Minimum/Maximum Query)问题——ST算法
- RMQ (Range Minimum/Maximum Query)问题
- RMQ(Range Minimum/Maximum Query)问题
- RMQ(Range Minimum Query)问题
- RMQ (Range Minimum/Maximum Query)算法(转)
- Range Minimum/Maximum Query (RMQ) - Sparse Table 算法
- RMQ (Range Minimum/Maximum Query)算法(转)
- RMQ(Range Minimum/Maximum Query)——ST算法
- RMQ (Range Minimum/Maximum Query)算法(转)