原題連接
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; }