bzoj1047/haoi2007 理想的正方形

题目描述

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

输入格式

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

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

输出格式

仅一个整数,为ab矩阵中所有“nn正方形区域中的最大整数和最小整数的差值”的最小值。

输入输出样例
输入 #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

分析

这题简直就是为了单调队列而单调队列(当然可以用st表或线段树)
简要讲讲思路吧。
f [ i ] [ j ] f[i][j] f[i][j]表示从 ( i , j ) (i,j) (i,j)这个点往右拓展 n n n 个所能得到的最小值, g [ i ] [ j ] g[i][j] g[i][j]为最大值。
假如我们已经得到了 f [ i ] [ j ] , g [ i ] [ j ] f[i][j],g[i][j] f[i][j],g[i][j],那么我们枚举每个点 ( i , j ) (i,j) (i,j)之后,是不是相当于在这个点往后 n n n个到 ( i + n , j ) (i+n,j) (i+n,j)这段区间里找到 f f f 数组的最小值和 g g g数组的最大值。那我们从每一列的第一行开始扫起,是不是就变成了一个大小为 n n n 的滑动窗口的问题,每次求窗口的最小最大值,而这正是单调队列能线性时间内解决的问题。
那么 f f f g g g 数组怎么得到呢?我们从一维来看,是不是相当于每次求 ( i , j ) (i,j) (i,j) ( i , j + n ) (i, j+n) (i,j+n)这段区间的最小和最大值?我们从第一列扫起,仍然是一个滑动窗口问题啊!用单调队列就可以求出 f f f g g g数组了!
所以我说这道题是单调队列题!

代码

#include <bits/stdc++.h>
#define N 1005
#define inf 2147483647
using namespace std;
int mp[N][N], f[N][N], g[N][N], q[N], x, y, p[N], a, b, ans = inf;
int main(){
	int i, j, n, m, k;
	scanf("%d%d%d", &n, &m, &k);
	for(i = 1; i <= n; i++)
	 	for(j = 1; j <= m; j++) scanf("%d", &mp[i][j]);
	for(i = 1; i <= n; i++){
		x = 1, y = 0;
		for(j = 1; j <= m; j++){
			q[++y] = j;
			if(x < y && (j - q[x] + 1) > k) x++;
			while(x < y && mp[i][q[y]] > mp[i][q[y-1]]) q[y-1] = q[y], y--;
			if(j >= k) f[i][j - k + 1] = mp[i][q[x]];
		}
	}
	for(i = 1; i <= n; i++){
		x = 1, y = 0;
		for(j = 1; j <= m; j++){
			q[++y] = j;
			if(x < y && (j - q[x] + 1) > k) x++;
			while(x < y && mp[i][q[y]] < mp[i][q[y-1]]) q[y-1] = q[y], y--;
			if(j >= k) g[i][j - k + 1] = mp[i][q[x]];
		}
	}
	//for(i = 1; i <= n; i++) printf("%d\n", f[i][1]);
	for(j = 1; j <= m - k + 1; j++){
		a = 1, b = 0;
		x = 1, y = 0;
		for(i = 1; i <= n; i++){
			q[++y] = i;
			p[++b] = i;
			if(x < y && (i - q[x] + 1) > k) x++;
			if(a < b && (i - p[a] + 1) > k) a++;
			while(x < y && f[q[y]][j] > f[q[y-1]][j]) q[y-1] = q[y], y--;
			while(a < b && g[p[b]][j] < g[p[b-1]][j]) p[b-1] = p[b], b--;
			if(i >= k){
				ans = min(ans, f[q[x]][j] - g[p[a]][j]);
			}
		}
	}
	printf("%d", ans);
	return 0;
}
各种题解及学习笔记~ 文章被收录于专栏

各种题解及学习笔记

全部评论

相关推荐

白火同学:1、简历可以浓缩成一页,简历简历先要“简”方便HR快速过滤出有效信息,再要“历”用有效信息突出个人的含金量。 2、教育背景少了入学时间~毕业时间,HR判断不出你是否为应届生。 3、如果你的平台账号效果还不错,可以把账号超链接或者用户名贴到对应位置,一是方便HR知道你是具体做了什么内容的运营,看到账号一目了然,二是口说无凭,账号为证,这更有说服力。
面试被问期望薪资时该如何...
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
2025-12-21 23:42
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务