题目
给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。
返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。
注意:本题相对书上原题稍作改动
示例:
输入:
[
[-1,0],
[0,-1]
]
输出:[0,1,0,1]
解释:输入中标粗的元素即为输出所表示的矩阵
说明:
1 <= matrix.length, matrix[0].length <= 200
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-submatrix-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
一维数组上,元素总和最大的连续子数组这个问题能用动态规划在 O ( n ) O(n) O(n) 的时间复杂度求解,那么现在就要把二维的问题转化成一维
对于二维的范围,假设子矩阵左上角的元素下标是
(
r
o
w
1
,
c
o
l
1
)
(row1,col1)
(row1,col1),子矩阵右下角的元素下标是
(
r
o
w
2
,
c
o
l
2
)
(row2,col2)
(row2,col2),那么子矩阵行的范围就是
[
r
o
w
1
,
r
o
w
2
]
[row1,row2]
[row1,row2],列的范围是
[
c
o
l
1
,
c
o
l
2
]
[col1,col2]
[col1,col2]。如果先用两层循环来枚举所有可能的行范围,在每一次两层循环内部,我们要做的事情就是求解出最佳的列范围。而如果行范围是已知的,我们就可以把
[
r
o
w
1
,
r
o
w
2
]
[row1,row2]
[row1,row2] 行中的元素在竖直方向上进行压缩成一维数组,数组中元素 nums[i]
的值表示矩阵在第
i
i
i 列上,
[
r
o
w
1
,
r
o
w
2
]
[row1,row2]
[row1,row2] 行所有元素的和,这就是一开始已知的问题,所以一共是三层循环,在每次得到最优解的时候维护 ans
的值即可
最后还有一个问题就是:如何快速得到压缩后的一维数组?这里首先遍历矩阵,得到矩阵在列方向上的前缀和,然后每次在两层循环内部用减法就能得到待处理的一维数组
时间复杂度:
O
(
n
3
)
O(n^3)
O(n3)
空间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
C++ 代码
class Solution {
public:
vector<int> getMaxMatrix(vector<vector<int>>& matrix) {
if (matrix.empty() || matrix[0].empty())
return vector<int>();
const int Y = matrix.size();
const int X = matrix[0].size();
// 得到列方向上的前缀和矩阵prefixSumCol
vector<vector<int>> prefixSumCol(Y, vector<int>(X, 0));
for (int x = 0; x < X; ++x) {
prefixSumCol[0][x] = matrix[0][x];
for (int y = 1; y < Y; ++y) {
prefixSumCol[y][x] = prefixSumCol[y - 1][x] + matrix[y][x];
}
}
int maxSum = matrix[0][0];
vector<int> ans(4, 0);
//对每一个行范围[i,j],寻找最优的列范围
for (int i = 0; i < Y; ++i) {
for (int j = i; j < Y; ++j) {
vector<int> sumsOfCol(X);
if (i == 0) {
for (int k = 0; k < X; ++k)
sumsOfCol[k] = prefixSumCol[j][k];
} else {
for (int k = 0; k < X; ++k)
sumsOfCol[k] = prefixSumCol[j][k] - prefixSumCol[i - 1][k];
}
vector<int> dp(X);
dp[0] = sumsOfCol[0];
int begin = 0;
if (dp[0] > maxSum)
ans = vector<int>{i, 0, j, 0};
for (int k = 1; k < X; ++k) {
if (dp[k - 1] < 0) {
dp[k] = sumsOfCol[k];
begin = k;
if (dp[k] > maxSum) {
maxSum = dp[k];
ans = vector<int>{i, k, j, k};
}
} else {
dp[k] = dp[k - 1] + sumsOfCol[k];
if (dp[k] > maxSum) {
maxSum = dp[k];
ans = vector<int>{i, begin, j, k};
}
}
}
}
}
return ans;
}
};