#include <iostream>
using namespace std;
// 定义矩阵的最大规模,这里设为 110,可根据实际情况调整
const int N = 110;
// 二维前缀和数组,dp[i][j] 表示从矩阵左上角(1,1)到(i,j)这个矩形区域内所有元素的和
int dp[N][N];
int main()
{
// 输入矩阵的边长 n
cin >> n;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
// 输入矩阵当前位置 (i,j) 的元素值
cin >> x;
// 计算二维前缀和,公式推导:
// dp[i][j] = 上方区域和(dp[i - 1][j]) + 左方区域和(dp[i][j - 1]) - 重复计算的左上角区域和(dp[i - 1][j - 1]) + 当前元素值(x)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + x;
}
}
// 初始化最大子矩阵和,这里用 -127 * N 是一种简单的赋初值方式,
// 假设矩阵中元素最小可能为 -127(题目未明确说明时的一种保守处理,保证能覆盖所有可能的较小值)
int ret = -127 * N;
// 枚举子矩阵的左上角坐标 (x1, y1)
for(int x1 = 1; x1 <= n; x1++)
for(int y1 = 1; y1 <= n; y1++)
// 枚举子矩阵的右下角坐标 (x2, y2),要求 x2 >= x1,y2 >= y1,保证子矩阵是合法的矩形区域
for(int x2 = x1; x2 <= n; x2++)
for(int y2 = y1; y2 <= n; y2++)
{
// 利用二维前缀和公式计算以 (x1,y1) 为左上角,(x2,y2) 为右下角的子矩阵的和
// 公式解释:
// dp[x2][y2] 是包含该子矩阵的大矩形和;
// dp[x1 - 1][y2] 是子矩阵上方区域的和,需要减去;
// dp[x2][y1 - 1] 是子矩阵左方区域的和,需要减去;
// dp[x1 - 1][y1 - 1] 是上方和左方区域重复减去的部分,需要加回来。
int current_sum = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
// 更新最大子矩阵和
ret = max(ret, current_sum);
}
// 输出最大子矩阵和
cout << ret << endl;
return 0;
}
- 二维前缀和计算:二维前缀和数组 dp 的构建是解决问题的基础。dp[i][j] 记录了从矩阵左上角 (1,1) 到 (i,j) 这个矩形区域内所有元素的累加和。通过递推公式 dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + x ,可以在 \(O(n^2)\) 的时间复杂度内完成前缀和数组的计算。这样后续计算任意子矩阵的和时,就可以通过前缀和快速推导,无需重复遍历子矩阵元素求和。
- 枚举子矩阵:代码通过四层循环枚举所有可能的子矩阵。外层两层循环 x1 和 y1 确定子矩阵的左上角坐标,内层两层循环 x2 和 y2 确定子矩阵的右下角坐标,并且通过 x2 >= x1 、y2 >= y1 的限制,确保枚举的是合法的、连续的矩形区域。
- 子矩阵和计算与更新最大值:对于每一个枚举到的子矩阵,利用预先计算好的二维前缀和数组,通过公式 dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1] 快速计算出该子矩阵的和。然后将这个和与当前记录的最大和 ret 比较,如果更大就更新 ret 。最终 ret 中保存的就是整个二维矩阵中所有可能子矩阵和的最大值。