📌  相关文章
📜  国际空间研究组织 | ISRO CS 2017 – 5 月 |问题 56(1)

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

国际空间研究组织 | ISRO CS 2017 – 5 月 |问题 56

题目描述

给定一个 $n\times n$ 的方阵 $mat$,其中 $mat_{i,j}$ 表示第 $i$ 行第 $j$ 列的元素,且矩阵元素值全部为非负整数。每个元素均表示该位置上有多少个金属。现在要从这个方阵中选择一个子矩阵出来,使得这个子矩阵中的元素之和最大,但需要满足以下条件:

  • 子矩阵大小必须是奇数($1\times1$ 也算),即子矩阵的行数和列数均为奇数;
  • 子矩阵中心位置必须为 $mat_{i,j}$。

请输出符合以上条件的子矩阵中元素之和的最大值。

输入格式

输入的第一行包含一个整数 $n$,表示方阵的大小。

接下来 $n$ 行,每行 $n$ 个非负整数,表示方阵中的元素。

输出格式

输出符合条件的子矩阵中元素之和的最大值。

样例输入
5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
样例输出
25
数据范围

$1\leqslant n\leqslant 2000$,矩阵中的每个元素均为非负整数,且不超过 $100$。

题解

这道题目看上去似乎很难,因为要求的是一个奇数大小的子矩阵,并且这个子矩阵的中心位置必须是 $mat_{i,j}$。如果采用暴力搜索的方式,那么最坏情况下时间复杂度为 $O(n^6)$,显然会超时。

不过我们可以用一些技巧来快速计算符合要求的子矩阵中元素之和的最大值。

首先,我们可以预处理一个前缀和矩阵 $s$,其中 $s_{i,j}$ 表示以 $(i,j)$ 为右下角的子矩阵的元素之和。这个矩阵可以通过以下代码计算得到:

s = [[0] * (n + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
    for j in range(1, n + 1):
        s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + mat[i - 1][j - 1]

这里需要注意,我们要把矩阵的下标从 $0$ 开始转换为从 $1$ 开始。

接下来,我们可以枚举每个 $mat_{i,j}$,然后利用前缀和矩阵 $s$ 快速计算出以 $mat_{i,j}$ 为中心的所有符合条件的子矩阵的元素之和。具体地,我们可以先枚举子矩阵的大小 $k$,由于我们要求的是一个奇数大小的子矩阵,因此 $k$ 也必须是奇数。然后我们可以计算以 $mat_{i,j}$ 为中心,大小为 $k\times k$ 的子矩阵的元素之和,即 $s_{i + k/2, j + k/2} - s_{i, j + k/2} - s_{i + k/2, j} + s_{i, j}$,其中 $k/2$ 表示整数除法。

最后,我们可以通过枚举每个 $mat_{i,j}$,以及每个奇数大小的子矩阵的方式,来计算符合条件的子矩阵中元素之和的最大值。

时间复杂度为 $O(n^2)$,空间复杂度为 $O(n^2)$。

参考代码
n = int(input())
mat = [list(map(int, input().split())) for _ in range(n)]

s = [[0] * (n + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
    for j in range(1, n + 1):
        s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + mat[i - 1][j - 1]

res = 0
for i in range(n):
    for j in range(n):
        for k in range(1, min(n - i, n - j) + 1, 2):
            sum = s[i + k // 2][j + k // 2] - s[i][j + k // 2] - s[i + k // 2][j] + s[i][j]
            res = max(res, sum)

print(res)

注意:以上示例代码中包含了输入和输出的部分,实际实现中可以根据需要进行调整。