给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
其中,A的子矩阵指在A中行和列均连续的一块。
输入描述:
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
输出描述:
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
解答:
二维前缀和:
首先,代码计算了一个二维前缀和数组 g[][]。对于数组 g 中的每个元素 g[i][j],它存储的是原矩阵从第一行到第 i 行,第一列到第 j 列的所有元素的和。
寻找最大子矩阵和:
接下来,代码使用三层嵌套循环来寻找最大子矩阵和。
外层循环枚举子矩阵的起始行 i。
第二层循环枚举子矩阵的结束行 j。
内层循环计算以 i 为起始行,j 为结束行的子矩阵的和 ans。
计算子矩阵和:
在内层循环中,ans 被用来累计当前子矩阵的和。每次迭代,ans 会加上 g[j][k] - g[i - 1][k],这实际上是计算了从第 i 行到第 j 行,第 k 列的子矩阵的和。
如果 ans 变为负数,则将其重置为0,因为负数会减少子矩阵的和。
如果当前子矩阵的和 ans 大于之前找到的最大和 sum,则更新 sum。
#include <iostream>
#include <string>
using namespace std;
int cal(int a[][502], int f[], int s[]) {
// cout << "从" << f[0] << "," << f[1] << "到" << s[0] << "," << s[1] << "————————————" << endl;
int all = 0;
for (int i = f[0]; i <= s[0]; i++) {
for (int j = f[1] ; j <= s[1]; j++) {
// cout << "a[" << i << "][" << j << "]==" << a[i][j] << endl;
all = all + a[i][j];
}
// cout << endl;
}
// cout << " all=" << all << endl;
return all;
}
int main() {
int n, m, ans, sum = 0;
cin >> n >> m;
int g[n + 1][m + 1];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
cin >> g[i][j];
g[i][j] += g[i - 1][j];
}
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; ++j) {//列举所有j-i行的情况
ans = 0;
for (int k = 1; k <= m; ++k) {//{从第一列开始,
//看后面每一列的列和是否为正,为正就加上,
//同时把当前ans存储给sum,防止此刻已经是最大的ans了
//不为正,我们就前功尽弃,从当前k继续往后找连续几列的列和都是整数的;}
ans = ans + g[j][k] - g[i - 1][k];//此处:
//是计算j-i这些行的列和
// _____________________________
// | / |
// | / |
// | / |
// | / |
// | i行 ____ |
// | / / \ / |
// | / / \/ |
// | / / j-i行 |
// | / / /\ |
// | / / /__\ |
// | j行 |
// | |
// | |
// |___________________________|
//加g[0]行->g[j]行的时候多加了一个g[0]行->g[i - 1]行
//所以要减去
if (ans > sum || sum == 0) {//把当前ans存储给sum
sum = ans;
}
if (ans < 0) {
ans = 0;
}//这个代码是精髓,意思是:
//从第一列开始往后推着走,
//如果某一列的列和小于零,
//那么这个和ans对于矩阵中最大和sum没有任何益处,
//而且还把我们之前连续几列积累的ans给破坏掉了,
//我们只有舍弃目前的ans,
//从当前k继续往后看有没有连续的最大值;
//同时我们需要在ans判断之前把当前ans存储起来,
//防止万一这个ans已经是最大的了;
}
}
}
cout << sum;
return 0;
}
如果使用四层嵌套则时间复杂度会达到O(n^3*m):
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
int cal(int a[][502], int f[], int s[]) {
// cout << "从" << f[0] << "," << f[1] << "到" << s[0] << "," << s[1] << "————————————" << endl;
int all = 0;
for (int i = f[0]; i <= s[0]; i++) {
for (int j = f[1] ; j <= s[1]; j++) {
// cout << "a[" << i << "][" << j << "]==" << a[i][j] << endl;
all = all + a[i][j];
}
// cout << endl;
}
// cout << " all=" << all << endl;
return all;
}
int main() {
int n, m;
cin >> n >> m;
int a[n][502];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
}
}
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < m; j++) {
// cout << a[i][j] << " ";
// }
// cout << endl;
// }
// int f[2], s[2];
// cout << "输入左上角坐标:";
// cin >> f[0] >> f[1];
// cout << "输入右下角坐标:";
// cin >> s[0] >> s[1];
// cout << "子矩阵的和为:\n" << cal(a, f, s) << endl;
int all_all = 0;
// cout << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
for (int c = i ; c < n; c++) {
for (int x = j; x < m; x++) {
int f[2], s[2];
f[0] = i;
f[1] = j;
s[0] = c;
s[1] = x;
// int sum = cal(a, f, s);
// if (sum > all_all) {
// all_all = sum;
// }
all_all = max(all_all, cal(a, f, s));
}
}
}
}
cout << all_all;
return 0;
}