P2216 [HAOI2007]理想的正方形

Link

题目描述

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

输入格式

第一行为 \(3\) 个整数,分别表示 \(a,b,n\) 的值

第二行至第 \(a+1\) 行每行为 \(b\) 个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

输出格式

仅一个整数,为 \(a\times b\) 矩阵中所有“ \(n\times n\) 正方形区域中的最大整数和最小整数的差值”的最小值。

输入输出样例

输入 #1

5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2

输出 #1

1

说明/提示

问题规模

(1)矩阵中的所有数都不超过1,000,000,000

(2)20%的数据2<=a,b<=100,n<=a,n<=b,n<=10

(3)100%的数据2<=a,b<=1000,n<=a,n<=b,n<=100

题解

这道题有很多种做法。然鹅我却写了最复杂的二维单调队列,不仅难写,还难调。蓝瘦

首先,我们可以求出以每个点为结尾的长度为 \(n\) 的区间中的最小值与最大值,也就是对每一行做一遍滑动窗口。

之后,我们对每一列在跑一边滑动窗口,只不过这次是对我们对每一行做单调队列的结果做一遍单调队列。

我们这一遍求出来的就是 以这个点为右下角,边长为 \(n\) 的这一块矩形中的最大值与最小值。

你可以理解为,把 \(n\) 个长度为 \(n\) 的区间拼在了一起。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int ans = 2147483647;
int n,m,k,a[1010][1010],q1[1010],q2[1010];
int maxn[1010][1010],minn[1010][1010];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
int main()
{
	n = read(); m = read(); k = 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++)
	{
		int l = 1, r = 0, L = 1, R = 0;
		q1[++r] = 1; q2[++R] = 1;
		for(int j = 2; j <= m; j++)//对每一行求一遍滑动窗口
		{
			while(l <= r && q1[l] <= j-k) l++;
			while(L <= R && q2[L] <= j-k) L++;
			while(l <= r && a[i][q1[r]] <= a[i][j]) r--;
			while(L <= R && a[i][q2[R]] >= a[i][j]) R--;
			q1[++r] = j; q2[++R] = j; 
			maxn[i][j] = a[i][q1[l]];
			minn[i][j] = a[i][q2[L]];	
		}
	}
	for(int i = k; i <= m; i++)
	{
		int l = 1, r = 0, L = 1, R = 0;
		q1[++r] = 1; q2[++R] = 1;
		for(int j = 2; j <= n; j++)//在每一行的基础上在对每一列做一遍滑动窗口
		{
			while(l <= r && q1[l] <= j-k) l++;
			while(L <= R && q2[L] <= j-k) L++;
			while(l <= r && maxn[q1[r]][i] <= maxn[j][i]) r--;
			while(L <= R && minn[q2[R]][i] >= minn[j][i]) R--;
			q1[++r] = j; q2[++R] = j;
			if(j >= k) ans = min(ans,maxn[q1[l]][i] - minn[q2[L]][i]);
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-09-18 22:18  genshy  阅读(180)  评论(0)    收藏  举报