问题描述
牛客版本
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵。 比如,如下4 * 4的矩阵 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2 的最大子矩阵是 9 2 -4 1 -1 8 这个子矩阵的大小是15。
输入描述:
输入是一个N * N的矩阵。输入的第一行给出N (0 < N <= 100)。 再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给出第二行的N个整数……)给出矩阵中的N2个整数,整数之间由空白字符分隔(空格或者空行)。 已知矩阵中整数的范围都在[-127, 127]。
输出描述:
输出最大子矩阵的大小。
输入样例
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
输出样例
15
力扣版本
给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。注意:本题相对书上原题稍作改动
样例
输入:
[
[-1,0],
[0,-1]
]
输出:[0,1,0,1]
解释:输入中标粗的元素即为输出所表示的矩阵
输入:
[
[9,-8,1,3,-2],
[-3,7,6,-2,4],
[6,-4,-4,8,-7]
]
输出:[0,0,2,3]
问题分析
首先明确解题方法,由于子问题之间明显有关联,在此数据量下直接暴力枚举必然超时,无明显贪心要素,子问题间有关联,考虑动态规划。
其次思考问题本质,此问题为连续子数组最大和的一个变式问题,是子数组的升维版本。因此可以考虑如何利用一维的去辅助求解当前二维的情况。
考虑所需元素,为了去快速得到和,避免运算过程中多次计算值,应用前缀和数组作为辅助。
最后考虑如何枚举所有情况。一个解答,也就是题目中所提到的矩阵,如果能够被确定,则至少需要四个参数。可以是第一个点下标,矩阵的大小;或者是第一个点和最后一个点的下标;或首行,尾行,首列,尾列。
为了方便枚举,我们采取枚举首行,尾行,首列,尾列的方式。同时以行为主体遍历,枚举不同列的值。
由于矩阵最小可为1*1,因此首行与尾行可相等。同理,首列与尾列可相同。
我们以i作为首行,j作为尾行。由题意可知,0 <= i <= j< n 。
那么如何枚举列呢?
如果我们此时将从i行到j行同一列的值都压缩到一个值上,那么实际上这个问题就转化为了一个一维的连续子数组最大和的问题。可以采取类似的方法求解,得到最大序列的起始下标和终止下标。
具体的编程细节不再赘述。
由于牛客版本的题目是力扣版本的特化情况,(牛客输入矩阵是n * n,力扣是n * m),所以代码请更多参考力扣版本。
代码
/*牛客版本*/
#include <cstdio>
#include <cstring>
int a[101][101];
int sum[101];
int n;
int main () {
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
for(int j = 0; j < n; ++j) {
scanf("%d", &a[i][j]);
}
}
int maxN = -32767;
//从第0行开始枚举
for(int i = 0; i < n; ++i) {
memset(sum, 0, sizeof(sum));//前缀和数组置0
for(int j = i; j < n; ++j) {
int tmp = 0;
for(int k = 0; k < n; ++k) {
sum[k] += a[j][k];//计算当前列的前缀和
tmp += sum[k];
if(maxN < tmp) {
maxN = tmp;
}
if(tmp < 0) {
tmp = 0;
}
}
}
}
printf("%d", maxN);
return 0;
}
//leetcode版本 更为通用的版本
vector<int> getMaxMatrix(vector<vector<int>>& matrix) {
vector<int> ans;
int n = matrix.size(), m = matrix[0].size();
int maxN = INT_MIN, tmp, begin;
vector<int> sum(m);
for(int i = 0; i < n; ++i) {
for(int &v:sum) v = 0;
for(int j = i; j < n; ++j) {
tmp = 0;
begin = 0;
for(int k = 0; k < m; ++k) {
sum[k] += matrix[j][k];
tmp += sum[k];
if(maxN < tmp) {
//cout << tmp << endl;
maxN = tmp;
ans = vector<int>{i, begin, j, k};
}
if(tmp < 0) {
tmp = 0;
begin = k + 1;
}
}
}
}
//cout << maxN << endl;
return ans;
}