持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,矩形区域不超过 K 的最大数值和[二维前缀和] - 掘金 (juejin.cn)
前言
前缀和能用空间换时间,大幅降低时间复杂度,甚至是解题关键。刨析问题的每一个要求,利用问题中的有利条件,来完成解题。
一、矩形区域不超过 K 的最大数值和
二、前缀和
target : 从矩阵圈一块子矩阵,这些矩阵和小于K,求里面的最大矩阵和。
1-圈子矩阵?确定首行首列尾行尾列,则圈一个子矩阵。
2-子矩阵和?可二维前缀和快速得到子矩阵和。
3-最大矩阵和?用一个变量记录最大值即可。
// 矩阵区域不超过K的最大数值和。
public class MaxSumSubmatrix {
/*
target : 从矩阵圈一块子矩阵,这些矩阵和小于K,求里面的最大矩阵和。
1-圈子矩阵?确定首行首列尾行尾列,则圈一个子矩阵。
2-子矩阵和?可二维前缀和快速得到子矩阵和。
3-最大矩阵和?用一个变量记录最大值即可。
*/
public int maxSumSubmatrix(int[][] matrix, int k) {
int m = matrix.length, n = matrix[0].length;
// 构建二维前缀和。
int[][] prefix = buildPrefix(matrix, m, n);
// 圈子矩阵:定义好首行首列/尾行尾列即可。
int max = 1 << 31;
for (int i = 0; i < m; i++) {
// bug2:尾列不能比首列还低.
for (int j = i; j < m; j++) {
for (int p = 0; p < n; p++) {
for (int q = p; q < n; q++) {
/*
计算当前子矩阵和:
prefix[j + 1][q + 1]
- prefix[j + 1][p] - prefix[i][q + 1]
+ prefix[i][p]。
*/
int sum = prefix[j + 1][q + 1]
- prefix[j + 1][p] - prefix[i][q + 1]
+ prefix[i][p];
// 判断其是否超过了k
if (sum == k) return k;
if (sum < k && sum > max) max = sum;
}
}
}
}
return max;
}
// 构建二维矩阵前缀和。
private int[][] buildPrefix(int[][] matrix, int m, int n) {
int[][] prefix = new int[m + 1][n + 1];
// 二维前缀和定义:prefix[i][j] = prefix[i-1][j] + prefix[i][j-1]
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// bug1:前缀和,除了前缀,还要加上自己。
// bug2:前缀和定义出错,加上左 + 上边 - 去重 + 自己
// prefix[i][j] = prefix[i - 1][j] + prefix[i][j - 1] + matrix[i - 1][j - 1];
prefix[i][j] = prefix[i][j - 1]
+ prefix[i - 1][j]
- prefix[i - 1][j - 1]
+ matrix[i - 1][j - 1];
}
}
return prefix;
}
public static void main(String[] args) {
new MaxSumSubmatrix().maxSumSubmatrix(new int[][]{{5, -4, -3, 4}, {-3, -4, 4, 5}, {5, 1, 5, -4}}, 3);
}
}
注:这里不能用dp来抛弃负数,不是单纯的求最大值,如果K为大负数,就凉凉。
总结
1)前缀和;
2)问题刨析。