原题连接
ios
给咱们一个n * m矩阵,要求咱们求出一个面积最大的子矩阵,知足其内部的极差小于等于c, 同时宽度小于等于100
算法
输入 m, n, c,求这个最大面积。n,m <= 700,c <= 10spa
若是是暴力枚举的话,每次须要枚举子矩阵,而且扫描一遍这个子矩阵,时间复杂度是没法经过的。
code
咱们考率另外一种枚举方式,咱们先枚举左边界l与右边界r,这样是100*m的复杂度。
队列
而后咱们要考虑的是在这个get
的长条矩阵中拿到一块符合要求的string
而后更新答案
it
若是能在线性时间内算出这个结果, 那么就是可行的算法。咱们首先能够对这个长条状的子矩阵进行预处理,对每一行都预处理出它的最大值和最小值。这个操做看似是 \(O((r - l + 1) * n)\) 的,可是由于咱们先枚举的左区间以后枚举的右区间,因此本次处理每行的最大最小值只须要将增长的有边界的一列数考虑进去就能够了,时间为 \(O(n)\)
io
以后这个子问题也就转化成了“一个序列中选出极差小于等于c的最长的一段子序列”。这是有关单向逐步移动的区间最值问题,因此可使用单调队列,能够知足在线性时间内完成这个任务,求出 \([bottom, top]\)
class
总时间复杂度为 O(100 * n * m) (可能这也是题目中限定宽度为100的缘由)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int m, n, c; int aa[705][705]; int maxl[705], minl[705]; int quemx[705], quemn[705]; int main() { while (scanf("%d%d%d", &m, &n, &c) == 3) { for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { scanf("%d", &aa[i][j]); } } int ans = 0; for (int l = 1; l <= m; ++l) { for (int i = 1; i <= n; ++i) { maxl[i] = aa[i][l]; minl[i] = aa[i][l]; } int mxr = min(m, l + 99); for (int r = l; r <= mxr; ++r) { for (int i = 1; i <= n; ++i) { maxl[i] = max(maxl[i], aa[i][r]); minl[i] = min(minl[i], aa[i][r]); } int hx = 1; int hn = 1; int tx = 1; int tn = 1; int ll = 1; for (int i = 1; i <= n; ++i) { while (hx != tx && maxl[quemx[tx - 1]] <= maxl[i]) { --tx; } quemx[tx++] = i; while (hn != tn && minl[quemn[tn - 1]] >= minl[i]) { --tn; } quemn[tn++] = i; while (hn != tn && hx != tx && maxl[quemx[hx]] - minl[quemn[hn]] > c) { ++ll; if (quemx[hx] < ll) { ++hx; } if (quemn[hn] < ll) { ++hn; } } ans = max(ans, (i - ll + 1) * (r - l + 1)); } } } printf("%d\n", ans); } return 0; }