• POJ-3264 RMQ


    题目链接:http://poj.org/problem?id=3264

    参考博客链接:https://blog.csdn.net/qq_31759205/article/details/75008659

    理解:题意求给定区域内最值之差。数据太大,暴力是行不通的,首先想到的是线段树,但RMQ实行和理解起来更简洁,就以新学的算法来写啦,所以等下细记录RMQ代码,略记录线段树代码,反正我的博客我做主,也就我一个人看。

    以下是线段树解法:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #define maxn 50005
     5 int a[maxn],treemax[maxn<<2],treemin[maxn<<2];//最大值和最小值树; 
     6 using namespace std;
     7 
     8 int max(int a,int b)
     9 {
    10     return a>b?a:b;
    11 }
    12 
    13 int min(int a,int b)
    14 {
    15     return a>b?b:a;
    16 }
    17 
    18 void build(int l,int r,int n)
    19 {
    20     if(l==r)
    21     {
    22         treemax[n]=a[l];
    23         treemin[n]=a[l];
    24         return;
    25     }
    26     int mid=(l+r)>>1;
    27     build(l,mid,n<<1);
    28     build(mid+1,r,n<<1|1);
    29     treemax[n]=max(treemax[n<<1],treemax[n<<1|1]);
    30     treemin[n]=min(treemin[n<<1],treemin[n<<1|1]);
    31 }//同时建立最大值最小值树; 
    32 
    33 int searchmax(int L,int R,int l,int r,int n)
    34 {
    35     if(l>=L&&r<=R)
    36     return treemax[n];
    37     int mid=(l+r)>>1,ans=-1;
    38     if(L<=mid)
    39     ans=max(ans,searchmax(L,R,l,mid,n<<1));
    40     if(mid<R)
    41     ans=max(ans,searchmax(L,R,mid+1,r,n<<1|1));
    42     return ans;
    43 }
    44 
    45 int searchmin(int L,int R,int l,int r,int n)
    46 {
    47     if(l>=L&&r<=R)
    48     return treemin[n];
    49     int mid=(l+r)>>1,ans=5000000;
    50     if(L<=mid)
    51     ans=min(ans,searchmin(L,R,l,mid,n<<1));
    52     if(mid<R)
    53     ans=min(ans,searchmin(L,R,mid+1,r,n<<1|1));
    54     return ans;
    55 }//最大值树和最小值树就注意ans的初始化了; 
    56 
    57 int main()
    58 {
    59     int m,n;
    60     while(~scanf("%d%d",&m,&n))
    61     {
    62         int l,f;
    63         for(int i=1;i<=m;i++)
    64         scanf("%d",&a[i]);
    65         build(1,m,1);
    66         while(n--)
    67         {
    68             scanf("%d%d",&l,&f);
    69             printf("%d
    ",searchmax(l,f,1,m,1)-searchmin(l,f,1,m,1));
    70         }
    71     }
    72     return 0;
    73 }

    对于RMQ,用了dp,用于解决区间最值问题很不错吧,只能感叹发明这算法的人厉害了,dp[i][j]的意思是对于第i个数,往后2的j次方个数里的最值是多少。先理清思路吧:

    1:状态转移方程可运用性:F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1]);因为对于每个dp所储存2^j范围的最值,都可以通过比较两段连续2^j-1范围的最值得到,第一段为以数i,范围j-1,即F[i,j-1],另一段为以数i+2^(j-1)(原数加原范围的一半),范围j-1,即F[i + 2^(j-1),j-1];

    2:查找可确定性:dp范围虽然是2^n,但可以实现任意区间的比较,利用了区间重复比较(即任意区间的表示都可以利用两个区间的并集来代替):比如区间(1,5)可以由(1,4)和(2,5)两个区间代替,这里就涉及到了代码中k值的计算(2^k因小于等于区间长度)。

    现在原理弄明白了(参考博客里有大牛解释),就上代码吧:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define maxn 50005
     6 int a[maxn],dpmax[maxn][20],dpmin[maxn][20];//最大值和最小值背包; 
     7 using namespace std;
     8 int max(int a,int b)
     9 {
    10     return a>b?a:b;
    11 }
    12 
    13 int min(int a,int b)
    14 {
    15     return a>b?b:a;
    16 }
    17 
    18 int search(int index,int left,int right)//index为1,表示要返回区间最大值,其他为最小值; 
    19 {
    20     int k=0;
    21     while((1<<(k+1))<=right-left+1) k++;//求k值,即所求的的范围2^k所不能超过,这样才能通过用两个比范围 小一级的范围代替; 
    22     if(index==1) 
    23     return max(dpmax[left][k],dpmax[right-(1<<k)+1][k]);
    24     else
    25     return min(dpmin[left][k],dpmin[right-(1<<k)+1][k]);//返回两个范围的最值; 
    26 }
    27 
    28 int main()
    29 {
    30     int m,n;
    31     while(~scanf("%d%d",&m,&n))
    32     {
    33         int left,right;
    34         for(int i=1;i<=m;i++)
    35         {
    36             scanf("%d",&a[i]);
    37             dpmax[i][0]=a[i];
    38             dpmin[i][0]=a[i];//初始化长度为1的背包 
    39         }
    40         for(int i=1;(1<<i)<=m;i++)//i代表长度,即为2^i长度 ,长度比总长度或等于总长度结束,从2长度开始; 
    41         for(int j=1;j+(1<<i)-1<=m;j++) //j代表从第j个数开始 (因为长度包括本身数) 
    42         {
    43             dpmax[j][i]=max(dpmax[j][i-1],dpmax[j+(1<<(i-1))][i-1]);
    44             dpmin[j][i]=min(dpmin[j][i-1],dpmin[j+(1<<(i-1))][i-1]);
    45         }//同时填满最大最小值背包; 
    46         for(int i=0;i<n;i++)
    47         {
    48             scanf("%d%d",&left,&right);
    49             printf("%d
    ",search(1,left,right)-search(0,left,right));
    50         }
    51     }
    52     return 0;
    53 }
    54 //笔记:x<<y代表x乘以2^y;

    总的就这样了;这题测试数据量很大,用cin会超时,还是老老实实用sp吧。

  • 相关阅读:
    obj文件可视化
    TypeError: unsupported operand type(s) for +: 'range' and 'range'
    ubuntu截屏软件shutter
    什么是Redis缓存穿透、缓存雪崩和缓存击穿
    在 ASP.NET Core 5.0 中访问 HttpContext
    如何使用带有BOM的UTF8编码的C#中的GetBytes()?
    ASP.NET Core 5.0 Web API 自动集成Swashbuckle
    ASP.NET Core 5.0 的新增功能
    面试被问到SQL | delete、truncate、drop 有什么区别?
    3个值得学习和练手的.net企业级开源项目,强烈推荐
  • 原文地址:https://www.cnblogs.com/wwq-19990526/p/8783287.html
Copyright © 2020-2023  润新知