給一個$n\times m$的二維矩陣,包含-1 0 +1,和一個$k$。
求一個最小的子矩陣滿足裡面的和至少為$k$。
$1\le n, m\le 500, 0\le k\le 2.5\times 10^5$
一個老套的子矩陣問題,首先枚舉一個維度的左右界,然後就能把那個左右界內的和都壓到剩下那維上,於是變成了一維的問題。
接著問題就變成給一個序列,問最小的subarray長度滿足和至少為$k$,如果序列都是正數就能用雙指針了,然而這題有負數。
怎麼辦呢? 先給他們做前綴和,然後枚舉右界$r$,之後就是找一個最大的 $l$ 滿足$sum_r-sum_l\ge k$,注意到如果有$l_1 < l_2$且$sum_{l_1}\ge sum_{l_2}$那$l_2$肯定優於$l_1$(因為可以用更小的區間長度湊出更大的區間和)。
這樣單調性就出來了! 維護一個單調遞增的stack,然後把上面式子整理一下得$sum_r-k\ge sum_l$,也就是要找出最不超過$sum_r-k$中最大的那個$sum_l$,就只要二分搜就好。
因為我是枚舉$n$那一維,所以我的複雜度是$\mathcal{O}(n^2mlogm)$。
關於實作的部分,因為根本不用存前綴和,所以我只用一個變數cur當前綴和用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
#include <bits/stdc++.h> using namespace std;
#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL); const int INF = 0x3F3F3F3F;
inline char owo(char c) { return c == '+' ? 1 : (c == '.' ? 0 : -1); }
const int N = 500; string arr[N]; int sum[N];
signed main() { EmiliaMyWife int n, m, nd; cin >> n >> m >> nd;
for(int i = 0; i < n; i++) cin >> arr[i];
int ans = INF;
vector<pair<int, int>> line; line.reserve(m);
for(int i = 0; i < n; i++) { memset(sum, 0, sizeof sum);
for(int j = i; j < n; j++) { for(int k = 0; k < m; k++) sum[k] += owo(arr[j][k]); int cur = 0; line.clear(); line.push_back({0, -1}); for(int k = 0; k < m; k++) { cur += sum[k]; auto it = upper_bound(line.begin(), line.end(), make_pair(cur - nd, INF)); if(it != line.begin()) ans = min(ans, (k - prev(it)->second) * (j - i + 1)); while(!line.empty() && line.back().first >= cur) line.pop_back(); line.push_back({cur, k}); } } }
if(ans == INF) cout << "-1\n"; else cout << ans << '\n'; }
|