【BZOJ 1047】 [HAOI2007]理想的正方形 单调队列(也可以dp水)

47 篇文章 0 订阅

BZOJ上面是总时间,所以这一道题在BZOJ上是可以直接用滚动数组水过去的,但是还是不推荐,不过还是写出来吧

#include<cstdio>
#include<cstring>
#include<iostream>
#define mmax(a,b,c,d) max(a,max(b,max(c,d)))
#define mmin(a,b,c,d) min(a,min(b,min(c,d)))
using namespace std;
int f[1024][1024],m,n,K,g[1024][1024],mat[1024][1024];
int read(){
    int x=0;
    char c=getchar();int flag=1;
    for(;c>'9'||c<'0';c=getchar()){if(c=='-')flag=-1;}
    for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
    x=x*flag;
    return x;
}
 
int main(){
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            mat[i][j]=read();   
            f[i][j]=g[i][j]=mat[i][j];
        }
    }
    for(int k=2;k<=K;k++){
        for(int i=1;i+k-1<=n;i++){
            for(int j=1;j+k-1<=m;j++){
                f[i][j]=mmax(mat[i][j],f[i+1][j],f[i][j+1],f[i+1][j+1]);
                g[i][j]=mmin(mat[i][j],g[i+1][j],g[i][j+1],g[i+1][j+1]);
            }
        }
    }
     
    int ans=1e9;
    for(int i=1;i+K-1<=n;i++){
        for(int j=1;j+K-1<=m;j++){
            ans=min(ans,f[i][j]-g[i][j]);
        }
    }
     
    printf("%d",ans);
    return 0;
}
好了,不要只是水题就满足了,正解应该是跑两次单调队列,一次计算一行连续k的最小最大,然后再利用跑出来的一列中连续k个元素的极值来跑行就可以计算出这一个矩阵了

#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 1020
using namespace std;
int n,m,k,mat[maxn][maxn],f[3][maxn][maxn];
pair<int,int> q[maxn*3];

void front(int pos){//0是最小值1是最大值 
	for(int i=1;i<=n;i++){
		int l=1,r=0;
		for(int j=1;j<=m;j++){
			while(l<=r&&j-q[l].first+1>k)l++;
			if(pos==0)while(l<=r&&q[r].second>=mat[i][j])r--;
			else while(l<=r&&q[r].second<=mat[i][j])r--;
			q[++r]=make_pair(j,mat[i][j]);
			f[pos][i][j]=q[l].second;
		}
	}
	
	for(int i=k;i<=m;i++){
		int l=1,r=0;
		for(int j=1;j<=n;j++){
			while(l<=r&&j-q[l].first+1>k)l++;
			if(pos==0)while(l<=r&&q[r].second>=f[pos][j][i])r--;
			else while(l<=r&&q[r].second<=f[pos][j][i])r--;
			q[++r]=make_pair(j,f[pos][j][i]);
			f[pos][j][i]=q[l].second;
		}
	}
}

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",&mat[i][j]);
	front(0),front(1);
	int ans=1e9;
	for(int i=k;i<=n;i++)
		for(int j=k;j<=m;j++)ans=min(ans,f[1][i][j]-f[0][i][j]);
	printf("%d",ans);
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值