洛谷P2216 【HAOI2007】 理想的正方形

题目传送门

感觉这还是一道挺不错的题的。这道题的思路是先处理行的最大值和最小值,再用行的最值处理一遍列的最值,如果要这一遍求的同时也是以这个位置为右下角的正方形的最值,那我们需要两次处理都使用滑动窗口,而且要两个同时跑。

不会滑动窗口的请自行百度qwq

具体细节及变量解释见代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=1000000000;
const int N=1010;
struct node
{
    int id,data;
}x[N],y[N];//x和y数组为滑动窗口用的单调队列,一个单调增一个单调减,id记录入队时间(其实就是位置) 
int n,m,q,ans=INF,a[N][N],maxn[2][N][N],minn[2][N][N];//
//n和m为原矩阵大小(a和b),q为求的正方形大小(题目中的k)
//maxn[0]和minn[0]记录第一次处理的结果,maxn[1]和minn[1]记录第二次处理的结果 
int read()
{
    char ch=getchar();int sum=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')sum=(sum<<3)+(sum<<1)+ch-'0',ch=getchar();
    return sum;
}//快读 
int main()
{
    n=read();m=read();q=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            a[i][j]=read();
    for(int i=1;i<=n;i++)
    //第一次处理,maxn[0][i][j]记录第i行中以到第j个数结束的连续q(或者k)个数的最大值,minn类似 
    {
        int fx=1,fy=1,rx=0,ry=0;
        for(int j=1;j<=m;j++)
        {
            int t=a[i][j];
            if(x[fx].id<max(0,j-q+1))fx++;if(y[fy].id<max(0,j-q+1))fy++;
            while(fx<=rx&&x[rx].data<t)rx--;x[++rx].data=t;x[rx].id=j;
            while(fy<=ry&&y[ry].data>t)ry--;y[++ry].data=t;y[ry].id=j;//以上为两个滑动窗口 
            maxn[0][i][j]=x[fx].data;minn[0][i][j]=y[fy].data;//更新数组 
        }
    }
    for(int j=1;j<=m;j++)//第二次处理,基本同第一次 
    {
        int fx=1,fy=1,rx=0,ry=0;
        for(int i=1;i<=n;i++)
        {
            int t=maxn[0][i][j],s=minn[0][i][j];
            if(x[fx].id<max(0,i-q+1))fx++;if(y[fy].id<max(0,i-q+1))fy++;
            while(fx<=rx&&x[rx].data<t)rx--;x[++rx].data=t;x[rx].id=i;
            while(fy<=ry&&y[ry].data>s)ry--;y[++ry].data=s;y[ry].id=i;
            maxn[1][i][j]=x[fx].data;minn[1][i][j]=y[fy].data;
        }
    }
    for(int i=q;i<=n;i++)
        for(int j=q;j<=m;j++)
            ans=min(ans,maxn[1][i][j]-minn[1][i][j]);//找到最优解 
    printf("%d\n",ans);return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值