• 算法竞赛模板 RMQ(计算区间最值)


    ①一维RMQ

    (1) dp[i,j] 表示从第i个数起连续2j个数中的(最大值min、最小值max、最大公约数gcd……),通过更改下列代码中的红色函数即可实现。

    (2) b数组放置所需查询的数列。

    const int MAX=305;
    int dp[MAX][20];
    int mm[MAX];
    void initrmq(int n,int b[])
    {
        mm[0]=-1;
        for(int i=1;i<=n;i++)
        {
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
            dp[i][0]=b[i];
        }
        for(int j=1;j<=mm[n];j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    }
    ll rmq(int x,int y)
    {
        int k=mm[y-x+1];
        return max(dp[x][k],dp[y-(1<<k)+1][k]);
    }

    ②二维RMQ

    给定一个n*m矩阵,每次询问左上角(r1,c1)到右下角(r2,c2)的子矩形中的(最大值min、最小值max、最大公约数gcd……)并输出。如果每次所询问的四个角有符合条件的数,输出yes,否则输出no。

    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int MAX=305;
    int val[MAX][MAX];
    int dp[MAX][MAX][9][9];//最大值
    int mm[MAX];
    void initRMQ(int n,int m)//m*n的矩阵
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                dp[i][j][0][0]=val[i][j];
        for(int ii=0;ii<=mm[n];ii++)
            for(int jj=0;jj<=mm[m];jj++)
                if(ii+jj)
                    for(int i=1;i+(1<<ii)-1<=n;i++)
                        for(int j=1;j+(1<<jj)-1<=m;j++)
                            if(ii)dp[i][j][ii][jj]=max(dp[i][j][ii-1][jj],dp[i+(1<<(ii-1))][j][ii-1][jj]);
                            else dp[i][j][ii][jj]=max(dp[i][j][ii][jj-1],dp[i][j+(1<<(jj-1))][ii][jj-1]);
    }
    int rmq(int x1,int y1,int x2,int y2)//所查询矩形区间内的最大值 左上角(x1,y1) -> 右上角(x2,y2) 
    {
        int k1=mm[x2-x1+1];
        int k2=mm[y2-y1+1];
        x2=x2-(1<<k1)+1;
        y2=y2-(1<<k2)+1;
        return max(max(dp[x1][y1][k1][k2],dp[x1][y2][k1][k2]),max(dp[x2][y1][k1][k2],dp[x2][y2][k1][k2]));
    }
    int main()
    {
        mm[0]=-1;
        for(int i=1;i<=MAX;i++)
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        int n,m,Q;
        int r1,c1,r2,c2;
        while(scanf("%d%d",&n,&m)==2)
        {
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    scanf("%d",&val[i][j]);
            initRMQ(n,m);
            scanf("%d",&Q);
            while(Q--)
            {
                scanf("%d%d%d%d",&r1,&c1,&r2,&c2);//左上角(r1,c1) -> 右上角(r2,c2) 
                if(r1>r2)swap(r1,r2);
                if(c1>c2)swap(c1,c2);
                int tmp=rmq(r1,c1,r2,c2);
                printf("%d ",tmp);
                if(tmp==val[r1][c1]||tmp==val[r1][c2]||tmp==val[r2][c1]||tmp==val[r2][c2])
                    printf("yes
    ");
                else printf("no
    ");
            }
        }
        return 0;
    }

    ③二维RMQ降维

    给定一个n*n(n<=500)的矩阵(即是正方形),每次询问以(x,y)为左上角,边长为s的正方形区域内的最大值。

    dp[i][j][k]:以(i,j)为左上角,边长为2^k的正方形区域内的最大值。

    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int MAX=505;
    int dp[MAX][MAX][10],mm[MAX],val[MAX][MAX];
    void initrmq(int n)
    {
        int lt,lb,rt,rb;
        for(int k=0;k<=mm[n];k++)
            for(int i=1;i+(1<<k)-1<=n;i++)
                for(int j=1;j+(1<<k)-1<=n;j++)
                    if(k==0)
                        dp[i][j][k]=val[i][j];
                    else
                    {
                        lt=dp[i][j][k-1];                  //左上角
                        lb=dp[i+(1<<k-1)][j][k-1];         //左下角
                        rt=dp[i][j+(1<<k-1)][k-1];         //右上角
                        rb=dp[i+(1<<k-1)][j+(1<<k-1)][k-1];//右下角
                        dp[i][j][k]=max(max(lt,lb),max(rt,rb));
                    }
    }
    int rmq(int x,int y,int s)
    {
        if(s==1)return val[x][y];
        int k=mm[s];
        int lt=dp[x][y][k];
        int lb=dp[x+s-(1<<k)][y][k];
        int rt=dp[x][y+s-(1<<k)][k];
        int rb=dp[x+s-(1<<k)][y+s-(1<<k)][k];
        return max(max(lt,lb),max(rt,rb));
    }
    int main()
    {
        int i,j,k,T;
        mm[0]=-1;
        for(i=1;i<=MAX;i++)
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++)
        {
            int n,q;
            scanf("%d%d",&n,&q);
            for(i=1;i<=n;i++)
                for(j=1;j<=n;j++)
                    scanf("%d",&val[i][j]);
            initrmq(n);
            printf("Case %d:
    ",cas);
            while(q--)
            {
                int x,y,s;
                scanf("%d%d%d",&x,&y,&s);
                printf("%d
    ",rmq(x,y,s));
            }
        }
        return 0;
    }
  • 相关阅读:
    Angular 学习笔记 (Material table sticky 原理)
    Asp.net core 学习笔记 ( ef core transaction scope & change level )
    sql server 学习笔记 (nested transaction 嵌套事务)
    html 图片文字并排显示
    Maven 的配置
    Eclipse的配置
    tomcat 的安装与配置
    java jdk的安装与配置
    javascript 拖拽
    html5 CSS input placeholder兼容性处理
  • 原文地址:https://www.cnblogs.com/kannyi/p/9802253.html
Copyright © 2020-2023  润新知