洛谷P2216 理想的正方形 单调队列

给出一个 n × m n×m n×m的矩阵,现在要你从里面找出一个 k × k k×k k×k的矩阵,使得这个矩阵的最大值减去最小值最大。 n , m ≤ 1 e 3 n,m\leq1e3 n,m1e3
如果只是一个序列,维护长度为 k k k的区间的最大值减去最小值。直接用两个单调队列分别维护这个滑动窗口即可。那么要维护一个矩形 k × n k×n k×n的矩形的最大值和最小值,我们可以把每个 k × 1 k×1 k×1的矩形压缩成一个点,最大值就是其中的最大值,最小值就是其中的最小值。
直接暴力求仍然是 O ( n m k ) O(nmk) O(nmk)的,不如对每一列用单调队列预处理出这一段的最大值和最小值。这样时间复杂度就是 O ( n m ) O(nm) O(nm)的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e3+7;
int mp[N][N];
int Ma[N][N],Mi[N][N];
int qmax[N],qmin[N];
int ma[N],mi[N];
int h1=1,t1=0;
int h2=1,t2=0;
int tot=0; 
int main() {
	int n,m,k;
	int ans=2e9; 
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			scanf("%d",&mp[i][j]);
		} 
	}
	for(int j=1;j<=m;j++) {
		tot=0;
		int h1=1,t1=0;
		int h2=1,t2=0;
		for(int i=1;i<=n;i++) {
			while(t1-h1+1>0&&mp[qmax[t1]][j]<=mp[i][j]) t1--;
			while(t2-h2+1>0&&mp[qmin[t2]][j]>=mp[i][j]) t2--;
			qmax[++t1]=i;
			qmin[++t2]=i;	
			while(t1-h1+1>0&&qmax[h1]<i-k+1) h1++;
			while(t2-h2+1>0&&qmin[h2]<i-k+1) h2++;
			if(i>=k) {
				++tot;
				Ma[tot][j]=mp[qmax[h1]][j];
				Mi[tot][j]=mp[qmin[h2]][j];
			}
		}
	}
	for(int i=1;i<=tot;i++) {
		for(int j=1;j<=m;j++) {
			ma[j]=Ma[i][j];
			mi[j]=Mi[i][j];
		}
		int h1=1,t1=0;
		int h2=1,t2=0;
		for(int j=1;j<=m;j++) {
			while(t1-h1+1>0&&ma[qmax[t1]]<=ma[j]) t1--;
			while(t2-h2+1>0&&mi[qmin[t2]]>=mi[j]) t2--;
			qmax[++t1]=j;
			qmin[++t2]=j;
			while(qmax[h1]<j-k+1) h1++;
			while(qmin[h2]<j-k+1) h2++;
			if(j>=k) {
				ans=min(ans,ma[qmax[h1]]-mi[qmin[h2]]);
			} 
		}
	}
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值