P2216 [HAOI2007]理想的正方形

P2216 [HAOI2007]理想的正方形

 

题目描述

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

a,b<=1000

 

分析题目: 首先可以想到一个O(a*b*n) 的解法:

我最开始想的办法是预处理递推出矩形中的最大值和最小值,即:用maxv(i,j,k)表示以点(i,j)为左上角的边长为k的矩形中的最大值,然后用递推公式

maxv[i][j] = max(grid[i][j], max(maxv[i+1][j+1], max(maxv[i+1][j], maxv[i][j+1])));

但是只有50分,接下来,有两种优化方法:

1.把n转化为log(n),用RMQ(倍增)

2.单调队列:每一行O(b)处理,然后每一列O(a)处理,复杂度应为O(a*b)

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;

#define res register int
#define getchar gc
char buf[1<<18],*fc,*tc;
inline char gc()
{
	if(fc==tc)
	{
		tc=(fc=buf)+fread(buf,1,1<<18,stdin);
		if(tc==fc) return EOF;
	}
	return *fc++;
}
inline int read()
{
	int x=0,f=1; char ch;
	while(!isdigit(ch=getchar())) if(ch=='-') f=-1;
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return f*x;
}

const int N=1005,inf=0x3f3f3f3f;
int a,b,n;
int mp[N][N],X[N][N],x[N][N],Y[N][N],y[N][N];
//大写最大值,小写最小值
//x[i][j]:第i行j~j+n-1的最小值
//y[i][j]:从mp[i~i+n-1][j~j+n-1]的最小值 

int Q[N],q[N],h,t,H,T;//head -> tail  单调  

inline void solve()
{
    for(res i=1 ; i<=a ; ++i)
    {
        h=t=H=T=Q[1]=q[1]=1;
        for(res j=2 ; j<=b ; ++j)
        {
            while(H<=T&&mp[i][j]>=mp[i][Q[T]]) --T;//维护单调性 
            while(h<=t&&mp[i][j]<=mp[i][q[t]]) --t;
            q[++t]=j; Q[++T]=j; 	
            //维护大小 
            while(j-Q[H]>=n) ++H;  
            while(j-q[h]>=n) ++h;
            if(j>=n) X[i][j-n+1]=mp[i][Q[H]],x[i][j-n+1]=mp[i][q[h]]; 
        }
    }
    
    for(res j=1 ; j+n-1<=b ; ++j)//维护j~j+n-1列 
    {
        h=t=H=T=Q[1]=q[1]=1;
        for(res i=2 ; i<=a ; ++i)
        {
            while(H<=T&&X[i][j]>=X[Q[T]][j]) --T;
            while(h<=t&&x[i][j]<=x[q[t]][j]) --t;
            Q[++T]=i; q[++t]=i;
            while(i-Q[H]>=n) ++H;
            while(i-q[h]>=n) ++h;
            if(i>=n) Y[i-n+1][j]=X[Q[H]][j],y[i-n+1][j]=x[q[h]][j];
        }
    }
}

int main()
{
    a=read(); b=read(); n=read();
    for(res i=1 ; i<=a ; ++i) 
		for(res j=1 ; j<=b ; ++j) mp[i][j]=read();
    solve();
    int ans=inf;
    for(res i=1 ; i+n-1<=a ; ++i)
        for(res j=1 ; j+n-1<=b ; ++j)
            ans=min(ans,Y[i][j]-y[i][j]);
    
    printf("%d\n",ans);
    return 0;
}

  

 

转载于:https://www.cnblogs.com/wmq12138/p/10691168.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值