leetcode
leetcode 1101 ~ 1150
最大的幻方

最大的幻方

难度:

标签:

题目描述

一个 k x k 的 幻方 指的是一个 k x k 填满整数的方格阵,且每一行、每一列以及两条对角线的和 全部相等 。幻方中的整数 不需要互不相同 。显然,每个 1 x 1 的方格都是一个幻方。

给你一个 m x n 的整数矩阵 grid ,请你返回矩阵中 最大幻方 的 尺寸 (即边长 k)。

 

示例 1:

输入:grid = [[7,1,4,5,6],[2,5,1,6,4],[1,5,4,3,2],[1,2,7,3,4]]
输出:3
解释:最大幻方尺寸为 3 。
每一行,每一列以及两条对角线的和都等于 12 。
- 每一行的和:5+1+6 = 5+4+3 = 2+7+3 = 12
- 每一列的和:5+5+2 = 1+4+7 = 6+3+3 = 12
- 对角线的和:5+4+3 = 6+4+2 = 12

示例 2:

输入:grid = [[5,1,3,1],[9,3,3,1],[1,3,3,8]]
输出:2

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 50
  • 1 <= grid[i][j] <= 106

代码结果

运行时间: 108 ms, 内存: 16.4 MB


/*
 * Approach using Java Streams:
 * We can also utilize Java Streams to streamline the computation of sums for rows, columns, and diagonals.
 * This approach primarily involves converting traditional for-loops into stream operations where possible.
 */
import java.util.stream.IntStream;

public class LargestMagicSquareStream {
    public int largestMagicSquare(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int maxK = Math.min(m, n);
        for (int k = maxK; k > 0; k--) {
            for (int i = 0; i + k <= m; i++) {
                for (int j = 0; j + k <= n; j++) {
                    if (isMagic(grid, i, j, k)) return k;
                }
            }
        }
        return 1;
    }

    private boolean isMagic(int[][] grid, int x, int y, int k) {
        int sum = IntStream.range(0, k).map(j -> grid[x][y + j]).sum();
        // Check rows
        if (!IntStream.range(0, k).allMatch(i -> IntStream.range(0, k).map(j -> grid[x + i][y + j]).sum() == sum)) return false;
        // Check columns
        if (!IntStream.range(0, k).allMatch(j -> IntStream.range(0, k).map(i -> grid[x + i][y + j]).sum() == sum)) return false;
        // Check diagonals
        int diag1 = IntStream.range(0, k).map(i -> grid[x + i][y + i]).sum();
        int diag2 = IntStream.range(0, k).map(i -> grid[x + i][y + k - 1 - i]).sum();
        return diag1 == sum && diag2 == sum;
    }
}

解释

方法:

这个解决方案首先计算了矩阵的行和列前缀和,以便快速计算任何子矩阵的行和列的和。然后,它尝试找到最大的幻方,从最大可能的边长开始,逐步减小边长,直到找到一个有效的幻方或者边长降到1。对于每个可能的边长,它会遍历所有可能的起始点,检查是否所有行、列以及对角线的和都等于第一行的和。如果找到这样的幻方,则立即返回这个边长。如果没有找到,最后返回1(因为任何1x1的子矩阵都是幻方)。

时间复杂度:

O(mn + min(m, n)^3)

空间复杂度:

O(mn)

代码细节讲解

🦆
在计算每个可能的幻方时,为什么选择从最大可能的边长开始递减检查,而不是从小到大增加边长进行检查?
选择从最大可能的边长开始递减检查的原因是为了尽快找到最大的幻方。如果从小到大增加边长进行检查,我们可能需要检查很多较小的边长幻方,直到达到最大边长。而从最大边长开始检查,则一旦找到一个有效的幻方,就可以立即返回这个边长,无需检查更小的边长,从而节省时间和计算资源。
🦆
在检查每行和每列的和是否相等时,如果发现某行或某列的和不符合标准和,是否有必要继续检查剩余行和列?
如果在检查过程中发现某行或某列的和不符合标准和,那么没有必要继续检查剩余的行和列。这是因为幻方的定义要求所有行、所有列以及对角线的和必须完全相等。一旦发现任何一行或一列不符合这一条件,当前的子矩阵就不可能是一个有效的幻方,因此可以立即停止当前的检查,继续尝试其他可能的子矩阵,这样做可以提高算法的效率。
🦆
算法中如何确保在计算对角线的和时不会出现索引越界的错误?
在计算对角线和的代码中,对角线元素的索引是通过循环变量`k`和边长`edge`来控制的。循环变量`k`从0开始,直到`edge-1`。对于主对角线,使用的索引是`grid[i + k][j + k]`,对于副对角线是`grid[i + k][j + edge - 1 - k]`。由于算法先检查了边长`edge`是否小于等于`m`和`n`(矩阵的行数和列数),保证了`i + edge <= m`和`j + edge <= n`,因此在索引时不会超出矩阵的边界,从而避免了索引越界的错误。
🦆
前缀和数组`rowsum`和`colsum`的初始化过程中,为什么选择先单独处理第一个元素,再处理剩余元素?
在初始化前缀和数组`rowsum`和`colsum`时,选择先单独处理第一个元素,再处理剩余元素的原因是为了设置基础值以便计算累加和。对于`rowsum`,第一个元素是直接从`grid`中获取的,之后的元素是基于前一个元素的值加上当前`grid`元素的值;对于`colsum`也是类似的处理。这种方式简化了累加过程,避免了在循环内部频繁地检查索引是否为0,从而提高了代码的简洁性和执行效率。

相关问题