题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1174
分析和思路:没什么难理解的。。(暴力就不再说了这题可以水过。。)
rmq(本质dp)
预处理:
设A[i]是要求区间最值的数列,F[i, j]表示从第i个数起连续2^j个数中的最大值。(DP的状态)
我们把F[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从 i 到i + 2 ^ (j - 1) - 1为一段,i + 2 ^ (j - 1)到i + 2 ^ j - 1为一段(长度都为2 ^ (j - 1))。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。F[i,j]就是这两段各自最大值中的最大值。于是我们得到了状态转移方程F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])。
查询:
假如我们需要查询的区间为(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])。
1 #include <iostream> 2 #include <algorithm> 3 #include <cmath> 4 #include <cstdio> 5 using namespace std; 6 const int maxn=1e4+5; 7 int a[maxn],f[10001][15]; 8 int n,x,y; 9 10 void rmq() 11 { 12 for(int i=1;i<=n;i++) f[i][0]=a[i];//长度为0就是本身 13 for(int j=1;j<=15;j++)//2的j次方<=maxn 14 { 15 for(int i=1;i<=n;i++) 16 { 17 if(i+(1<<j)-1<=n)//从i开始长度为1<<j的,所以-1 18 f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);//分成2段长度一样的 19 } 20 } 21 } 22 23 int main() 24 { 25 cin>>n; 26 for(int i=1;i<=n;i++) cin>>a[i]; 27 rmq(); 28 29 int m; 30 cin>>m; 31 while(m--) 32 { 33 cin>>x>>y; 34 x++; y++; 35 36 int k=log2(y-x+1); 37 int ans=max(f[x][k],f[y-(1<<k)+1][k]);//将区间覆盖完全,用y减保证了不会超过 38 cout<<ans<<endl; 39 } 40 41 return 0; 42 }
线段树
#include <iostream> #include <algorithm> #include <stdio.h> using namespace std; const int maxn=1e4+5; int n,a,b,m,ans; struct node { int l,r,w,f; }tree[maxn*4]; void build(int k,int ll,int rr)//建树 { tree[k].l=ll,tree[k].r=rr; if(tree[k].l==tree[k].r) { scanf("%d",&tree[k].w); return; } int m=(ll+rr)/2; build(k*2,ll,m); build(k*2+1,m+1,rr); tree[k].w=max(tree[k*2].w,tree[k*2+1].w); } void ask_interval(int k)//区间查询 { if(tree[k].l>=a&&tree[k].r<=b) { ans=max(ans,tree[k].w); return; } int m=(tree[k].l+tree[k].r)/2; if(a<=m) ask_interval(k*2); if(b>m) ask_interval(k*2+1); } int main() { scanf("%d",&n); build(1,1,n); scanf("%d",&m); for(int i=1;i<=m;i++) { ans=0; scanf("%d%d",&a,&b);//区间查询 a++; b++; ask_interval(1); printf("%d\n",ans); } return 0; }
完