51nod1174区间中最大的数(rmq或线段树)

题目链接: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;
}

猜你喜欢

转载自www.cnblogs.com/redblackk/p/9543947.html