給定一個正整數、負整數和 0 組成的 N × M 矩陣,編寫代碼找出元素總和最大的子矩陣。
返回一個數組 [r1, c1, r2, c2],其中 r1, c1 分別代表子矩陣左上角的行號和列號,r2, c2 分別代表右下角的行號和列號。若有多個滿足條件的子矩陣,返回任意一個均可。
leetcode
解題思路:
- 首先為了在O(1)的時間內獲取兩個點之間的矩陣和,就需要求每個點的前綴和,然后通過前綴和的是運算,快速獲取兩個點構成的矩陣的和。
- 然后是遍歷整個數組,但是如果依次枚舉每兩個點的話,時間復雜度是n的四次方的,會超時。所以,這里需要利用動態規划的思想。如果讓一個維度不變,就可以把問題變為一維數組求和最小的連續字串問題,當前綴小於零,那就只會給后面添累贅,所以直接就去掉。
class Solution {
public int[] getMaxMatrix(int[][] matrix) {
int n = matrix.length;
if(n == 0) return new int[0];
int m = matrix[0].length;
int[][] f = new int[n + 1][m + 1];
int[][] s = new int[n + 1][m + 1];
// 求二維的前綴和
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + matrix[i - 1][j - 1];
}
}
int[] res = new int[4];
int sum = matrix[0][0];
// 先枚舉一個維度,再枚舉另一個維度,這樣就將為一維數組求和最大的連續子串問題。
for(int i = 1; i < n + 1; i++) {
for(int j = 0; j < i; j++) {
int start = 0;
for(int k = 1; k < m + 1; k ++) {
int cur = s[i][k] - s[j][k] - s[i][start] + s[j][start];
if(cur > sum) {
sum = cur;
res = new int[]{j, start, i - 1, k - 1};
}
// 如果前面的和小於零,只會拖累后面,索性直接去掉前面的
if(cur < 0) start = k;
}
}
}
return res;
}
}