【2019牛客暑期多校训练营 第三场 F题】【Planting Trees】【单调队列】

本文探讨了一个算法问题,即在一个N行N列的矩阵中寻找最大子矩阵,使得子矩阵内的最大值与最小值之差不超过给定数值M。通过使用单调队列优化算法效率,实现了解决方案并提供了详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:
https://ac.nowcoder.com/acm/contest/883/F
题意:
给一个N行N列的矩阵和一个数M,求最大的子矩阵使得该矩阵中最大值和最小值之差不超过M。
类似题目:
洛谷P2216 [HAOI2007]理想的正方形
洛谷P2219 [HAOI2007]修筑绿化带
codeforces Hello 2015 (Div.1) C题 Subrect Query (和本题几乎一样,求的东西不一样)
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pi 3.1415926
#define mp(x, y) make_pair(x, y)
#define vi vector<int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 5e2 + 5;

int T, N, M;
int grap[MAX][MAX], Mx[MAX], Mn[MAX], q1[MAX], q2[MAX];
//q1维护最小值递增队列
//q2维护最大值递减队列
//均记录位置
int main() {
#ifdef ACM_LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &N, &M);
		for (int i = 1; i <= N; i++)
			for (int j = 1; j <= N; j++)
				scanf("%d", &grap[i][j]);
		int ans = 0;
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++)
				Mx[j] = Mn[j] = grap[i][j];
			for (int j = i; j <= N; j++) {

				//Mx[k]为[i,j]行k列的最大值,Mn[k]为[i,j]行k列的最小值
				for (int k = 1; k <= N; k++)
					Mx[k] = max(Mx[k], grap[j][k]), Mn[k] = min(Mn[k], grap[j][k]);

				if ((j - i + 1) * N <= ans)continue;//剪枝操作:(j - i + 1)行N列矩阵满足情况下都比已知答案小

				int h1 = 1, h2 = 1, t1 = 0, t2 = 0;//队列head 和 tail
				for (int l = 1, r = 1; r <= N; r++) {
					while (h1 <= t1 && Mn[q1[t1]] >= Mn[r])t1--;//当前值 比递增队列 队尾 更小
					q1[++t1] = r;
					while (h2 <= t2 && Mx[q2[t2]] <= Mx[r])t2--;//当前值 比递减队列 队尾 更大
					q2[++t2] = r;
					while (l <= r && Mx[q2[h2]] - Mn[q1[h1]] > M) {//如果 区间内 最值之差不满足条件,说明区间过长了
						//为什么能代表整个子矩阵呢,因为当前行的值是最值(在前几行的基础上取最值得来),而且队列是单调的,队头最大/最小,所以队头的值就是当前整个子矩阵的最值
						l++;
						while (h1 <= t1 && q1[h1] < l)h1++;//保证队头在区间内
						while (h2 <= t2 && q2[h2] < l)h2++;//保证队头在区间内
					}
					ans = max(ans, (j - i + 1) * (r - l + 1));// (j - i + 1)就是矩阵的行数, (r - l + 1)就是矩阵的列数
					if ((j - i + 1) * (N - l + 1) <= ans)break;//剪纸条件: 剩下的也不能再超过已知最大值
				}
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值