zoukankan      html  css  js  c++  java
  • BZOJ1047

    原题链接

    题意简述

    有一个由n×m个整数组成的矩阵,现请你从中找出一个k×k的方阵,使得该方阵所有数中的最大值和最小值的差最小。

    分析

    首先处理出c1[i][j]表示max{a[i][j..(j+k1)]}c2[i][j]表示min{a[i][j..(j+k1)]}。也就是从(i,j)向右连续k个数中的最值。
    然后以(i,j)为左上角的k×k方阵中的最大值就是max{c1[i..(i+k1)][j]},最小值就是min{c2[i..(i+k1)][j]}。因为(i,j)向右k个数加上(i+1,j)向右k个数加上…加上(i+k1,j)向右k个数就是以(i,j)为左上角的k×k方阵。所有最大值与最小值的差中的最小值就是答案。
    以上都可以通过单调队列实现。
    总时间复杂度O(nm)

    实现

    开两个单调队列,一个递增,一个递减。

    代码

    //[HAOI2007]理想的正方形
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int const N=1e3+10;
    int const INF=0x7FFFFFFF;
    int n,m,k,a[N][N];
    int c[2][N][N];
    struct queue
    {
        int op,cl,type;
        struct ele{int x,y,val;} qu[N];
        void clear() {op=1,cl=0;}
        void push(int x,int y,int val)
        {
            if(type==0) while(qu[cl].val<=val && cl>=op) cl--;
            else while(qu[cl].val>=val && cl>=op) cl--;
            cl++; qu[cl].x=x,qu[cl].y=y,qu[cl].val=val;
        }
        int topX() {return qu[op].x;}
        int topY() {return qu[op].y;}
        int topV() {return qu[op].val;}
    
    }q[2];
    void push1(int x,int y) {q[0].push(x,y,a[x][y]); q[1].push(x,y,a[x][y]);}
    void push2(int x,int y) {q[0].push(x,y,c[0][x][y]); q[1].push(x,y,c[1][x][y]);}
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
        q[0].type=0; q[1].type=1;
        for(int i=1;i<=n;i++)
        {
            q[0].clear(); q[1].clear();
            for(int j=1;j<=k;j++) push1(i,j);
            c[0][i][1]=q[0].topV();
            c[1][i][1]=q[1].topV();
            for(int j=2;j<=m-k+1;j++)  
            {
                if(q[0].topY()<j) q[0].op++;
                if(q[1].topY()<j) q[1].op++;
                push1(i,j+k-1);
                c[0][i][j]=q[0].topV();
                c[1][i][j]=q[1].topV();
            }
        }
        int ans=INF;
        for(int j=1;j<=m-k+1;j++)
        {
            q[0].clear(); q[1].clear();
            for(int i=1;i<=k;i++) push2(i,j);
            ans=min(ans,q[0].topV()-q[1].topV());
            for(int i=2;i<=n-k+1;i++)
            {
                if(q[0].topX()<i) q[0].op++;
                if(q[1].topX()<i) q[1].op++;
                push2(i+k-1,j);
                ans=min(ans,q[0].topV()-q[1].topV());
            }
        }
        printf("%d",ans);
        return 0;
    }

    注意

    代码可能很难写的简洁…我尽力了

  • 相关阅读:
    高放的c++学习笔记之函数基础
    高放的c++学习笔记之关联容器
    高放的c++学习笔记之lambda表达式
    二分图小结
    送给大一学弟学妹的几句话
    网络流小结
    后缀数组小结
    hdu5353
    UVALive 5792 Diccionário Portuñol
    概率dp小结
  • 原文地址:https://www.cnblogs.com/VisJiao/p/8485769.html
Copyright © 2011-2022 走看看