zoukankan      html  css  js  c++  java
  • 【刷题】【deque优化dp】理想的正方形

    有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

    (1)矩阵中的所有数都不超过1,000,000,000

    (2)20%的数据2<=a,b<=100,n<=a,n<=b,n<=10

    (3)100%的数据2<=a,b<=1000,n<=a,n<=b,n<=100

    首先想出的是st表形式,然后用了3维表达,加上了快读卡常:

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    int a,b,n;
    const int N=1003,M=103;
    int mn[N][N][M],mx[N][N][M];
    
    inline int read()
    {
        int x=0;char c=getchar();
        while(c<'0' || c>'9') c=getchar();
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
        return x;
    } 
    
    int main()
    {
        a=read(),b=read(),n=read();
        for(int i=1;i<=a;i++)
            for(int j=1;j<=b;j++)
                mx[i][j][1]=mn[i][j][1]=read();
        
        for(int i=2;i<=n;i++)
            for(int sx=a-i+1;sx;sx--)
                for(int sy=b-i+1;sy;sy--)
                {
                    mn[sx][sy][i]=min( min(mn[sx][sy][i-1],mn[sx+1][sy+1][i-1]) ,min(mn[sx][sy+1][i-1],mn[sx+1][sy][i-1]) );
                    mx[sx][sy][i]=max( max(mx[sx][sy][i-1],mx[sx+1][sy+1][i-1]) ,max(mx[sx][sy+1][i-1],mx[sx+1][sy][i-1]) );
                }
                
        int ans=1<<30;
        for(int sx=a-n+1;sx;sx--)
            for(int sy=b-n+1;sy;sy--)
                ans=min(ans,mx[sx][sy][n]-mn[sx][sy][n]);
        
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    然后结果:20->20

    卡常无效,估计是原始代码太慢了

    然后按着题解减了一维......

    20->60,神奇!

    所以减去一维有可能大程度加快速度

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    int a,b,n;
    const int N=1003,M=103;
    int mn[N][N],mx[N][N];
    
    inline int read()
    {
        int x=0;char c=getchar();
        while(c<'0' || c>'9') c=getchar();
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
        return x;
    } 
    
    int main()
    {
        a=read(),b=read(),n=read();
        for(int i=1;i<=a;i++)
            for(int j=1;j<=b;j++)
                mx[i][j]=mn[i][j]=read();
        
        for(int i=2;i<=n;i++)
        {
            int r1=a-i+1;
            for(int sx=1;sx<=r1;sx++)
            {
                int r2=b-i+1;
                for(int sy=1;sy<=r2;sy++)
                {
                    mn[sx][sy]=min( min(mn[sx][sy],mn[sx+1][sy+1]) ,min(mn[sx][sy+1],mn[sx+1][sy]) );
                    mx[sx][sy]=max( max(mx[sx][sy],mx[sx+1][sy+1]) ,max(mx[sx][sy+1],mx[sx+1][sy]) );
                }
            }
        }
                
        int ans=1<<30;
        for(int sx=a-n+1;sx;sx--)
            for(int sy=b-n+1;sy;sy--)
                ans=min(ans,mx[sx][sy]-mn[sx][sy]);
        
        printf("%d
    ",ans);
        return 0;
    }

    然后一个快读,60->70

    最后一个O2,过了,

    然后去测别的评测机速度,不开O2全过......

    所以减维技巧要学好

    好了接下来是正经优化,

    (1)RMQ

    将O(n)的维护时间变成O(logn)

    注意:log2和log都是关键词,不能做变量名

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int a,b,n;
    const int N=1003,M=103;
    int lg[N],d[8];
    int mn[N][N][8],mx[N][N][8];
    
    inline int read()
    {
        int x=0;char c=getchar();
        while(c<'0' || c>'9') c=getchar();
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
        return x;
    }
    
    int main()
    {
        a=read(),b=read(),n=read();
        for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
        d[0]=1; for(int i=1;i<=lg[n];i++) d[i]=d[i-1]<<1;
        for(int i=1;i<=a;i++)
            for(int j=1;j<=b;j++)
                mx[i][j][0]=mn[i][j][0]=read();
        
        int ans=1<<30,t1=lg[n],t2=d[t1];
        for(int i=1;i<=t1;i++)
        {
            int r1=a-d[i]+1;
            for(int sx=1;sx<=r1;sx++)
            {
                int r2=b-d[i]+1;
                for(int sy=1;sy<=r2;sy++)
                {
                    mn[sx][sy][i]=min( min(mn[sx][sy][i-1],mn[sx+d[i-1]][sy+d[i-1]][i-1]) ,min(mn[sx][sy+d[i-1]][i-1],mn[sx+d[i-1]][sy][i-1]) );
                    mx[sx][sy][i]=max( max(mx[sx][sy][i-1],mx[sx+d[i-1]][sy+d[i-1]][i-1]) ,max(mx[sx][sy+d[i-1]][i-1],mx[sx+d[i-1]][sy][i-1]) );
                }
            }
        } 
    
        for(int sx=a-n+1,ex=a+1;sx>0;sx--,ex--)
            for(int sy=b-n+1,ey=b+1;sy>0;sy--,ey--)
            {
                int a1=max( max(mx[sx][sy][t1],mx[sx][ey-t2][t1] ),max(mx[ex-t2][sy][t1],mx[ex-t2][ey-t2][t1]) );
                int a2=min( min(mn[sx][sy][t1],mn[sx][ey-t2][t1] ),min(mn[ex-t2][sy][t1],mn[ex-t2][ey-t2][t1]) );
                ans=min(ans,a1-a2);
            }
        
        printf("%d
    ",ans);
        return 0;
    }

    (2)deque

    当做二位的线性单调dp来做

    复制自luogu:

    我的思路是...用单调队列分别维护行与列。

    具体实现方法:是先用单调队列对每一行的值维护,并将a[][]每个区间的最大值,最小值分别存在X[][]和x[][]中。

    那么X[][]与x[][]所存储的分别是1×n的长方形内的最大值,最小值。X[i][j]存储第i行第j~j+n-1列的长方形中的最大值。同理,x[i][j]存储第i行第j~j+n-1列的长方形中的最小值。

    这时再对这两个数组的每一列上的值进行维护,将X[][]中每个区间的的最大值用Y[][]维护,将x[][]中的每个区间的最小值用y[][]维护。那么Y[i][j]存储X[][]中第i~i+n-1行第j列的长方形的最大值。同理y[i][j]存储x[][]中第i~i+n-1行第j列的长方形的最小值。

    故Y[i][j]存储的实为以a[i~i+n-1][j~j+n-1]中的最大,即以i,j为左上角,边长为n的正方形中的最大值。同理,y[i][j]存储的即以i,j为左上角,边长为n的正方形中的最小值。

    #include<cstdio>
    #include<cstdlib>
    #include<queue>
    #include<algorithm>
    using namespace std;
    int a,b,n;
    const int N=1003,M=103;
    int d[N][N];
    int mx[2][N][N],mn[2][N][N];//0: (i,j)表示的是max(1,j-n+1)到j的 的最值,所以有效部分从n开始 
    
    deque <int > q1,q2; 
    
    int main()
    {
        scanf("%d%d%d",&a,&b,&n);
        for(int i=1;i<=a;i++)
        {
            q1.clear() ,q2.clear() ;
            for(int j=1;j<=b;j++)
            {
                scanf("%d",&d[i][j]);
                
                if(!q1.empty() && q1.front() <= j-n) q1.pop_front() ;
                while(!q1.empty() && d[i][q1.back() ]<=d[i][j] ) q1.pop_back() ;
                q1.push_back(j);
                mx[0][i][j]=d[i][q1.front() ]; 
                
                if(!q2.empty() && q2.front() <= j-n) q2.pop_front() ;
                while(!q2.empty() && d[i][q2.back() ]>=d[i][j] ) q2.pop_back() ;
                q2.push_back(j);
                mn[0][i][j]=d[i][q2.front() ]; 
            }
        }
        
        for(int j=1;j<=b;j++)
        {
            q1.clear() ,q2.clear() ;
            for(int i=1;i<=a;i++)
            {
                if(!q1.empty() && q1.front() <= i-n) q1.pop_front() ;
                while(!q1.empty() && mx[0][q1.back() ][j]<=mx[0][i][j] ) q1.pop_back() ;
                q1.push_back(i);
                mx[1][i][j]=mx[0][q1.front() ][j]; 
                
                if(!q2.empty() && q2.front() <= i-n) q2.pop_front() ;
                while(!q2.empty() && mn[0][q2.back() ][j]>=mn[0][i][j] ) q2.pop_back() ;
                q2.push_back(i);
                mn[1][i][j]=mn[0][q2.front() ][j]; 
            }
        }    
        
        int ans=mx[1][n][n]-mn[1][n][n];
        for(int i=n;i<=a;i++)
            for(int j=n;j<=b;j++)
                ans=min(ans,mx[1][i][j]-mn[1][i][j]);
        printf("%d
    ",ans);
        return 0;
    } 
    View Code
  • 相关阅读:
    MySql插不进中文,以及IDEA连接MySql出现小段乱码问题
    安装Consul环境并启动
    格式化日期,首字母大写
    设计模式
    用户刷新页面,中止客户端面请求,怎么判断服务端也取消请求
    Token使用
    IdentityServer4搭建和使用
    Filter 过滤器
    添加Filter 并控制它的执行顺序
    在startup 添加数据库链接字符串
  • 原文地址:https://www.cnblogs.com/xwww666666/p/11693051.html
Copyright © 2011-2022 走看看