📜  门|门 CS 1997 |第 58 题(1)

📅  最后修改于: 2023-12-03 15:42:20.948000             🧑  作者: Mango

《门|门 CS 1997 |第 58 题》

这道题是一道经典的矩阵题目,需要求给定矩阵的最大子矩阵和。如果您是初学者,可以把它作为您的入门练习;如果您是高手,这道题目也是一道很不错的优化练习。

问题描述

给定一个 $n\times m$ 的矩阵 $a_{i,j}$ ($1\leq n,m\leq 5000$),请计算矩阵的最大子矩阵和。

解法思路

最大子矩阵和问题,可以使用动态规划求解。具体做法是:

  1. 枚举矩阵的上下边界 $i, j$,设矩阵 $K$ 为行从 $i$ 到 $j$,列从 $1$ 到 $m$ 的矩阵,则最大子矩阵和为 $\sum_{k=1}^m{d_k}$,其中 $d_k$ 表示矩阵 $K$ 内的列 $k$ 形成的连续子序列的最大和;

  2. 对于一个列 $k$,设其在矩阵 $K$ 内形成的连续子序列的最大和为 $b_k$,则有 $b_k=\max{b_{k-1}+a_{j,k},a_{j,k}}$($b_0=0$),即我们可以利用动态规划求出每个 $b_k$。

  3. 然后,我们就可以在枚举上下边界 $i, j$ 的同时,求出能构成的所有矩阵的最大子矩阵和,最终返回全局最大子矩阵和即可。

代码实现

下面是 C++ 的代码实现:

#include <bits/stdc++.h>
using namespace std;

const int N = 5000 + 5;

int n, m;
int a[N][N], dp[N], ans = -0x7fffffff;

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) 
            scanf("%d", &a[i][j]);
    for (int i = 1; i <= n; i++) {
        memset(dp, 0, sizeof(dp));
        for (int j = i; j <= n; j++) {
            int sum = 0;
            for (int k = 1; k <= m; k++) {
                dp[k] = max(dp[k-1]+a[j][k], a[j][k]);
                sum += dp[k];
                ans = max(ans, sum);
                if (sum < 0) sum = 0;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}
总结

最大子矩阵和的问题应用广泛,掌握了该算法对程序员的编程水平也有提升作用。