标签:
【实现代码】
1 /* 2 pku3264 3 大意是给你一串数字,然后问你从第i个到第j个中最大的数减去最小的数的值 4 用rmq求出【i,j】中的最大最小值相减即可 5 rmq算法思想: 6 一,预处理 7 设a[i]是要求区间最值的数列,f[i,j]表示从第i个数起连续2^j个数中的最大值。 8 例如数列3 2 4 5 6 8 1 2 9 7 ,f[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。 9 f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……从这里可以看出f[i,0]其实就等于a[i]。 10 这样,Dp的状态、初值都已经有了,剩下的就是状态转移方程。 11 我们把f[i,j]平均分成两段(因为f[i,j] 一定是偶数个数字), 12 从i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。 13 用上例说明,当 i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。 14 f[i,j]就是这两段的最大值中的最大值。 15 于是我们得到了动规方程F[i,j]=max(F[i,j-1],F[i+2^(j-i),j-1]). 16 二,查询 17 如在上例中我们要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2,2]和f[5,2]得到。扩展到一般情况,就是把区间[l,r]分成两个长度为2^k的区间(保证有f[i,j]对应)。直接给出表达式: 18 k:=trunc(ln(r-l+1)/ln(2)); 19 ans:=max(F[l,k],F[r-2^k+1,k]); 20 */ 21 #include <iostream> 22 #include <math.h> 23 #define max(a,b) ((a>b)?a:b) 24 #define min(a,b) (a<b?a:b) 25 26 using namespace std; 27 28 const int maxn=50001; 29 int h[maxn]; 30 int mx[maxn][16],mn[maxn][16]; 31 int n,q; 32 33 void rmq_init() 34 { 35 int i,j,t; 36 for(j=1;j<=n;j++) mx[j][0]=mn[j][0]=h[j]; 37 int m=floor(log((double)n)/log(2.0)); 38 for(i=1;i<=m;i++){ 39 for(j=1;j<=n;j++){ 40 t = j+(1<<(i-1)); 41 if(t<=n) mx[j][i]=max(mx[j][i-1],mx[t][i-1]); 42 else mx[j][i]=mx[j][i-1]; 43 } 44 } 45 for(i=1;i<=m;i++){ 46 for(j=1;j<=n;j++){ 47 t = j+(1<<(i-1)); 48 if(t<=n) mn[j][i]=min(mn[j][i-1],mn[t][i-1]); 49 else mn[j][i]=mn[j][i-1]; 50 } 51 } 52 } 53 54 int rmq(int l,int r) 55 { 56 int m=floor(log((double)(r-l+1))/log(2.0)); 57 int a=max(mx[l][m],mx[r-(1<<m)+1][m]); 58 int b=min(mn[l][m],mn[r-(1<<m)+1][m]); 59 return a-b; 60 } 61 62 int main() 63 { 64 int i,l,r; 65 scanf("%d%d",&n,&q); 66 for(i=1;i<=n;i++) scanf("%d",&h[i]); 67 rmq_init(); 68 for(i=0;i<q;i++){ 69 scanf("%d%d",&l,&r); 70 printf("%d\n",rmq(l,r)); 71 } 72 return 0; 73 }
【例题】NYOJ-1185 最大最小值
如果C等于1,输出第L个数到第R个数之间的最小值;
如果C等于2,输出第L个数到第R个数之间的最大值;
如果C等于3,输出第L个数到第R个数之间的最小值与最大值的和。
2 4 1 3 2 4 2 1 1 4 2 2 3 5 1 2 3 4 5 1 3 1 5
1 3 6
1 /* 2 pku3264 3 大意是给你一串数字,然后问你从第i个到第j个中最大的数减去最小的数的值 4 用rmq求出【i,j】中的最大最小值相减即可 5 rmq算法思想: 6 一,预处理 7 设a[i]是要求区间最值的数列,f[i,j]表示从第i个数起连续2^j个数中的最大值。 8 例如数列3 2 4 5 6 8 1 2 9 7 ,f[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。 9 f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……从这里可以看出f[i,0]其实就等于a[i]。 10 这样,Dp的状态、初值都已经有了,剩下的就是状态转移方程。 11 我们把f[i,j]平均分成两段(因为f[i,j] 一定是偶数个数字), 12 从i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。 13 用上例说明,当 i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。 14 f[i,j]就是这两段的最大值中的最大值。 15 于是我们得到了动规方程F[i,j]=max(F[i,j-1],F[i+2^(j-i),j-1]). 16 二,查询 17 如在上例中我们要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2,2]和f[5,2]得到。扩展到一般情况,就是把区间[l,r]分成两个长度为2^k的区间(保证有f[i,j]对应)。直接给出表达式: 18 k:=trunc(ln(r-l+1)/ln(2)); 19 ans:=max(F[l,k],F[r-2^k+1,k]); 20 */ 21 #include <iostream> 22 #include <math.h> 23 #define max(a,b) ((a>b)?a:b) 24 #define min(a,b) (a<b?a:b) 25 26 using namespace std; 27 28 const int maxn=50001; 29 int h[maxn]; 30 int mx[maxn][16],mn[maxn][16]; 31 int n,q; 32 33 void rmq_init() 34 { 35 int i,j,t; 36 for(j=1;j<=n;j++) mx[j][0]=mn[j][0]=h[j]; 37 int m=floor(log((double)n)/log(2.0)); 38 for(i=1;i<=m;i++){ 39 for(j=1;j<=n;j++){ 40 t = j+(1<<(i-1)); 41 if(t<=n) mx[j][i]=max(mx[j][i-1],mx[t][i-1]); 42 else mx[j][i]=mx[j][i-1]; 43 } 44 } 45 for(i=1;i<=m;i++){ 46 for(j=1;j<=n;j++){ 47 t = j+(1<<(i-1)); 48 if(t<=n) mn[j][i]=min(mn[j][i-1],mn[t][i-1]); 49 else mn[j][i]=mn[j][i-1]; 50 } 51 } 52 } 53 54 int rmq(int l,int r) 55 { 56 int m=floor(log((double)(r-l+1))/log(2.0)); 57 int a=max(mx[l][m],mx[r-(1<<m)+1][m]); 58 int b=min(mn[l][m],mn[r-(1<<m)+1][m]); 59 return a-b; 60 } 61 62 int main() 63 { 64 int i,l,r; 65 scanf("%d%d",&n,&q); 66 for(i=1;i<=n;i++) scanf("%d",&h[i]); 67 rmq_init(); 68 for(i=0;i<q;i++){ 69 scanf("%d%d",&l,&r); 70 printf("%d\n",rmq(l,r)); 71 } 72 return 0; 73 }
标签:
原文地址:http://www.cnblogs.com/zhengbin/p/4438068.html