@@ -6,13 +6,13 @@ Tag : 「二分」、「前缀和」
66
77
88
9- 给你一个 m x n 的矩阵 matrix 和一个整数 k ,找出并返回矩阵内部矩形区域的不超过 k 的最大数值和。
9+ 给你一个 ` m x n ` 的矩阵 ` matrix ` 和一个整数 $k$ ,找出并返回矩阵内部矩形区域的不超过 $k$ 的最大数值和。
1010
11- 题目数据保证总会存在一个数值和不超过 k 的矩形区域。
12- 13- 11+ 题目数据保证总会存在一个数值和不超过 $k$ 的矩形区域。
1412
1513示例 1:
14+ ![ ] ( https://assets.leetcode.com/uploads/2021/03/18/sum-grid.jpg )
15+ 1616```
1717输入:matrix = [[1,0,1],[0,-2,3]], k = 2
1818
@@ -28,11 +28,11 @@ Tag : 「二分」、「前缀和」
2828```
2929
3030提示:
31- * m == matrix.length
32- * n == matrix[ i] .length
33- * 1 <= m, n <= 100
34- * -100 <= matrix[ i] [ j ] <= 100
35- * -10ドル^5$ <= k <= $ 10^5$
31+ * $ m == matrix.length$
32+ * $ n == matrix[ i] .length$
33+ * $ 1 <= m, n <= 100$
34+ * $ -100 <= matrix[ i] [ j ] <= 100$
35+ * -10ドル^5 <= k <= 10^5$
3636
3737---
3838
@@ -46,8 +46,8 @@ Tag : 「二分」、「前缀和」
4646
4747数据范围是 10ドル^2,ドル对应的计算量是 10ドル^8,ドル理论上会超时,但当我们枚举「矩形左上角」$(i,j)$ 的时候,我们只需要搜索位于 $(i,j)$ 的右下方的点 $(p,q)$ 作为「矩形右下角」,所以其实我们是取不满 $m^2 * n^2$ 的,但仍然具有超时风险(2021年04月20日 Java 测试可通过,C++ 使用 ` vector ` 会 TLE)。
4848
49- 代码:
50- ``` Java []
49+ Java 代码:
50+ ``` Java
5151class Solution {
5252 public int maxSumSubmatrix (int [][] mat , int k ) {
5353 int m = mat. length, n = mat[0 ]. length;
@@ -74,6 +74,35 @@ class Solution {
7474 }
7575}
7676```
77+ C++ 代码:
78+ ``` C++
79+ int sum[110 ][110 ];
80+ class Solution {
81+ public:
82+ int maxSumSubmatrix(vector<vector<int >>& mat, int k) {
83+ int m = mat.size(), n = mat[ 0] .size();
84+ for(int i = 1; i <= m; i++){
85+ for(int j = 1; j <= n; j++){
86+ sum[ i] [ j ] = sum[ i - 1] [ j ] + sum[ i] [ j - 1 ] - sum[ i - 1] [ j - 1 ] + mat[ i - 1] [ j - 1 ] ;
87+ }
88+ }
89+ int ans = INT_MIN;
90+ for(int i = 1; i <= m; i++){
91+ for(int j = 1; j <= n; j++){
92+ for(int p = i; p <= m; p++){
93+ for(int q = j; q <= n; q++){
94+ int cur = sum[ p] [ q ] - sum[ i - 1] [ q ] - sum[ p] [ j - 1 ] + sum[ i - 1] [ j - 1 ] ;
95+ if(cur <= k){
96+ ans = max(ans,cur);
97+ }
98+ }
99+ }
100+ }
101+ }
102+ return ans;
103+ }
104+ };
105+ ```
77106* 时间复杂度:预处理前缀和数组复杂度为 $O(m * n),ドル查找答案的复杂度为 $O(m^2 * n^2)$。整体复杂度为 $O(m^2 * n^2)$。
78107* 空间复杂度:$O(m * n)$
79108
149178
150179至此,我们通过预处理前缀和 + 容斥原理彻底将题目转化为「一维问题」进行来求解。
151180
152- 代码:
153- ``` Java []
181+ Java 代码:
182+ ```Java
154183class Solution {
155184 public int maxSumSubmatrix(int[][] mat, int k) {
156185 int m = mat.length, n = mat[0].length;
@@ -190,6 +219,38 @@ class Solution {
190219 }
191220}
192221```
222+ C++ 代码:
223+ ``` C++
224+ class Solution {
225+ public:
226+ int maxSumSubmatrix(vector<vector<int >>& mat, int k) {
227+ int m = mat.size(), n = mat[ 0] .size();
228+ vector<vector<int >> sum(m + 1,vector<int >(n + 1,0));
229+ for(int i = 1; i <= m; i++){
230+ for(int j = 1; j <= n; j++){
231+ sum[ i] [ j ] = sum[ i - 1] [ j ] + sum[ i] [ j - 1 ] - sum[ i - 1] [ j - 1 ] + mat[ i - 1] [ j - 1 ] ;
232+ }
233+ }
234+ int ans = INT_MIN;
235+ for(int top = 1; top <= m; top++){
236+ for(int bot = top; bot <= m; bot++){
237+ set<int > st;
238+ st.insert(0);
239+ for(int r = 1; r <= n; r++){
240+ int right = sum[ bot] [ r ] - sum[ top - 1] [ r ] ;
241+ auto left = st.lower_bound(right - k);
242+ if(left != st.end()){
243+ int cur = right - * left;
244+ ans = max(ans,cur);
245+ }
246+ st.insert(right);
247+ }
248+ }
249+ }
250+ return ans;
251+ }
252+ };
253+ ```
193254* 时间复杂度:枚举上下边界复杂度为 $O(m^2)$;枚举右边界为 $O(n),ドル使用 `TreeSet`(基于红黑树)存储和查找左边界复杂度为 $O(\log{n})$。整体复杂度为 $O(m^2 * n\log{n})$
194255* 空间复杂度:$O(m * n)$
195256
@@ -201,8 +262,8 @@ class Solution {
201262
202263事实上,我们需要将「二分过程」应用到数值较大的行或者列之中,这样才能最大化我们查找的效率(同时也回答了本题的进阶部分)。
203264
204- 代码:
205- ``` Java []
265+ Java 代码:
266+ ```Java
206267class Solution {
207268 public int maxSumSubmatrix(int[][] mat, int k) {
208269 int m = mat.length, n = mat[0].length;
@@ -234,6 +295,39 @@ class Solution {
234295 }
235296}
236297```
298+ C++ 代码:
299+ ``` C++
300+ class Solution {
301+ public:
302+ int maxSumSubmatrix(vector<vector<int >>& mat, int k) {
303+ int m = mat.size(), n = mat[ 0] .size();
304+ vector<vector<int >> sum(m + 1,vector<int >(n + 1,0));
305+ for(int i = 1; i <= m; i++){
306+ for(int j = 1; j <= n; j++){
307+ sum[ i] [ j ] = sum[ i - 1] [ j ] + sum[ i] [ j - 1 ] - sum[ i - 1] [ j - 1 ] + mat[ i - 1] [ j - 1 ] ;
308+ }
309+ }
310+ bool isRight = n > m;
311+ int ans = INT_MIN;
312+ for(int i = 1; i <= (isRight ? m : n); i++){
313+ for(int j = i; j <= (isRight ? m : n); j++){
314+ set<int > st;
315+ st.insert(0);
316+ for(int fixed = 1; fixed <= (isRight ? n : m); fixed++){
317+ int a = isRight ? sum[ j] [ fixed ] - sum[ i - 1] [ fixed ] : sum[ fixed] [ j ] - sum[ fixed] [ i - 1 ] ;
318+ auto b = st.lower_bound(a - k);
319+ if(b != st.end()){
320+ int cur = a - * b;
321+ ans = max(ans,cur);
322+ }
323+ st.insert(a);
324+ }
325+ }
326+ }
327+ return ans;
328+ }
329+ };
330+ ```
237331* 时间复杂度:预处理「每行」或「每列」的前缀和,复杂度为 $O(m * n)$;枚举子矩阵的「上下行」或「左右行」,复杂度为 $O(\min(m, n)^2)$;结合二维前缀和套用一维最大连续子数组解决方案,复杂度为$\max(m, n)\log{\max(m, n)}$。整体复杂度为 $O(\min(m, n)^2 * \max(m, n)\log{\max(m, n)})$
238332* 空间复杂度:$O(m * n)$
239333
@@ -245,8 +339,8 @@ class Solution {
245339
246340因此我们可以将计算前缀和的逻辑下放到搜索子矩阵的循环里去做,从而将 $O(m * n)$ 的空间复杂度下降到 $O(\max(m,n))$。
247341
248- 代码:
249- ``` Java []
342+ Java 代码:
343+ ```Java
250344class Solution {
251345 public int maxSumSubmatrix(int[][] mat, int k) {
252346 int m = mat.length, n = mat[0].length;
@@ -275,18 +369,49 @@ class Solution {
275369 }
276370}
277371```
372+ C++ 代码:
373+ ``` C++
374+ class Solution {
375+ public:
376+ int maxSumSubmatrix(vector<vector<int >>& mat, int k) {
377+ int m = mat.size(), n = mat[ 0] .size();
378+ bool isRight = n > m;
379+ vector<int > sum((isRight ? n + 1 : m + 1), 0);
380+ int ans = INT_MIN;
381+ for(int i = 1; i <= (isRight ? m : n); i++){
382+ fill(sum.begin(),sum.end(),0);
383+ for(int j = i; j <= (isRight ? m : n); j++){
384+ set<int > st;
385+ st.insert(0);
386+ int a = 0;
387+ for(int fixed = 1; fixed <= (isRight ? n : m); fixed++){
388+ sum[ fixed] += isRight ? mat[ j - 1] [ fixed - 1 ] : mat[ fixed - 1] [ j - 1 ] ;
389+ a += sum[ fixed] ;
390+ auto b = st.lower_bound(a - k);
391+ if(b != st.end()){
392+ int cur = a - * b;
393+ ans = max(ans,cur);
394+ }
395+ st.insert(a);
396+ }
397+ }
398+ }
399+ return ans;
400+ }
401+ };
402+ ```
278403* 时间复杂度:预处理「每行」或「每列」的前缀和,复杂度为 $O(m * n)$;枚举子矩阵的「上下行」或「左右行」,复杂度为 $O(\min(m, n)^2)$;结合二维前缀和套用一维最大连续子数组解决方案,复杂度为$\max(m, n)\log{max(m, n)}$。整体复杂度为 $O(\min(m, n)^2 * \max(m, n)\log{\max(m, n)})$
279404* 空间复杂度:$O(\max(m, n))$
280405
281406---
282407
283408### 最后
284409
285- 这是我们「刷穿 LeetCode」系列文章的第 ` No.363 ` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完 。
410+ 这是我们「刷穿 LeetCode」系列文章的第 `No.363` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完 。
286411
287412在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
288413
289- 为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode。
414+ 为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode。
290415
291416在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
292417
0 commit comments