zoukankan      html  css  js  c++  java
  • 【洛谷P2216】[HAOI2007]理想的正方形

    理想的正方形

      【题目描述】

      一个a*b的矩阵,从中取一个n*n的子矩阵,使所选矩阵中的最大数与最小数的差最小。

      思路:

      二维的滑动窗口

      对于每行:用一个单调队列维护,算出每个长度为n的区间的最大值和最小值,分别存在两个数组fmin和fmax中,fmax[i][j]表示第i行区间[j,j+n-1]的最大值。

      对于每列:用一个单调队列维护,算出fmax和fmin数组中纵列每个长度为n的区间的最大值和最小值,分别存在两个数组ffmin和ffmax中,

    ffmax[i][j]表示以(i,j)为左上端点的大小为n*n的矩阵中的最大值。

      扫一遍ffmax[1~a-n+1][1~b-n+1]和ffmin[1~a-n+1][1~b-n+1]的差,得出ans。

     

      单调队列原理:

      以维护最大值为例:

      对于每个新加入区间的值,显而易见的是:对于向右移动的“窗口”,即当前长度为n的区间中,若存在data[q[i]]比data[q[j]]大且q[i]>q[j](q[i]表示队列中的第i个元素的编号,data[i]表示编号为i的元素的值),则可以保证q[j]在以后的区间取最大值时是不会产生影响的,我们便可以将q[j]删除,从而得到更加优秀的时间复杂度,所以,当一个新的值入队时,便可以将在其前面入队且值比它小的元素删除。 我们便可以发现可以用一个单调队列维护。 当然,对于不在当前区间内的“老”元素,要把它从队列中删除。

     

    贴C++代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int a,b,n;
    int g[1001][1001],fmin[1001][1001],fmax[1001][1001],ffmin[1001][1001],ffmax[1001][1001],queue[1001],head,tail,i,j,ans=0x7fffffff;
    int main()
    {
        scanf("%d%d%d",&a,&b,&n);
        for(i=1;i<=a;i++)
         for(j=1;j<=b;j++)
          scanf("%d",&g[i][j]);
        for(i=1;i<=a;i++)    //枚举每行  单调递减求区间最大值 
        {
            head=1;tail=0;
            memset(queue,0,sizeof(queue));
            for(j=1;j<n;j++)
            {
                while(tail>0&&g[i][queue[tail]]<g[i][j]) tail--;
                queue[++tail]=j;
            }
            for(j=n;j<=b;j++)
            {
                while(tail>=head&&g[i][queue[tail]]<g[i][j]) tail--;
                queue[++tail]=j;
                if(queue[head]<j-n+1) head++;
                fmax[i][j-n+1]=g[i][queue[head]];
            }
        }
        for(i=1;i<=a;i++)    //枚举每行  单调递增求区间最小值 
        {
            head=1;tail=0;
            memset(queue,0,sizeof(queue));
            for(j=1;j<n;j++)
            {
                while(tail>0&&g[i][queue[tail]]>g[i][j]) tail--;
                queue[++tail]=j;
            }
            for(j=n;j<=b;j++)
            {
                while(tail>=head&&g[i][queue[tail]]>g[i][j]) tail--;
                queue[++tail]=j;
                if(queue[head]<j-n+1) head++; 
                fmin[i][j-n+1]=g[i][queue[head]];
            }
        }
        for(i=1;i<=b-n+1;i++)    //枚举每列  单调递减求区间最大值
        {
            head=1;tail=0;
            memset(queue,0,sizeof(queue));
            for(j=1;j<n;j++)
            {
                while(tail>0&&fmax[queue[tail]][i]<fmax[j][i]) tail--;
                queue[++tail]=j;
            }
            for(j=n;j<=a;j++)
            {
                while(tail>=head&&fmax[queue[tail]][i]<fmax[j][i]) tail--;
                queue[++tail]=j;
                if(queue[head]<j-n+1) head++;
                ffmax[j-n+1][i]=fmax[queue[head]][i];
            }
        }
        for(i=1;i<=b-n+1;i++)    //枚举每列  单调递增求区间最小值 
        {
            head=1;tail=0;
            memset(queue,0,sizeof(queue));
            for(j=1;j<n;j++)
            {
                while(tail>0&&fmin[queue[tail]][i]>fmin[j][i]) tail--;
                queue[++tail]=j;
            }
            for(j=n;j<=a;j++)
            {
                while(tail>=head&&fmin[queue[tail]][i]>fmin[j][i]) tail--;
                queue[++tail]=j;
                if(queue[head]<j-n+1) head++;
                ffmin[j-n+1][i]=fmin[queue[head]][i];
            }
        }
        for(i=1;i<=a-n+1;i++)
         for(j=1;j<=b-n+1;j++)
          ans=min(ans,ffmax[i][j]-ffmin[i][j]);
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    java爬取Excel表格
    drf-view
    django--View
    tornado的Application的一些事儿
    tornado的路由分发
    线程和asyncio的比较
    GIL
    else的使用
    协程
    生成器代替迭代器
  • 原文地址:https://www.cnblogs.com/yjkhhh/p/8490577.html
Copyright © 2011-2022 走看看