首页 > 试题广场 >

最大子矩阵

[编程题]最大子矩阵
  • 热度指数:132 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
小猿发现了一根侧面写着数字的圆柱,仔细观察发现柱子的侧面被分割成了N行M列的若干个小格子,每个格子里写着一个数字。
你能帮助小猿找到圆柱侧面的最大子矩阵吗?

输入描述:
第一行输入两个数N M (1 ≤ N ≤ 100, 1 ≤ M ≤ 1000)
接下来N行,每行输入M个数字


输出描述:
请输出圆柱体侧面的最大子矩阵和
示例1

输入

2 3
2 1 2
3 -2 4

输出

11

说明

因为是圆柱体侧面,矩阵第一列和第三列相连,因此最大子矩阵为[[2, 2], [4, 3]],矩阵和为11

备注:
圆柱体侧面矩阵的第一列和最后一列相连
使用python运行超时的考生可以尝试切换使用pypy
其实就是二维数组版连续子数组最大和,利用动态规划求解。
(1)合并行和,构造一个辅助数组calSum,其中calSum[i][j]表示第j列前i行的元素之和;
(2)然后用两重循环遍历行范围的可能性,循环内部化简为一维数组版的连续子数组的最大和。
由于题中表示这个二维数组是个圆柱面,因此需要考虑环的问题。这里我们采用一个取巧的方式去避开环的情况,用动态规划求解连续列子数组的最大和和最小和,其中最大和是不考虑环的情况,用所有列的总和减去最小和就是考虑环时候的最大和,选择这两种情况大的那个即可。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] params = br.readLine().split(" ");
        int m = Integer.parseInt(params[0]);
        int n = Integer.parseInt(params[1]);
        int[][] matrix = new int[n][n];
        for(int i = 0; i < m; i++){
            String[] row = br.readLine().split(" ");
            for(int j = 0; j < n; j++)
                matrix[i][j] = Integer.parseInt(row[j]);
        }
        int[][] calSum = new int[m][n];     // calSum[i][j]表示前i行第j列的元素之和
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++)
                calSum[i][j] = (i != 0? calSum[i - 1][j]: 0) + matrix[i][j];
        }
        int maxSize = Integer.MIN_VALUE;
        int[] colSum = new int[n];
        for(int startRow = -1; startRow < m; startRow ++){
            for(int endRow = startRow + 1; endRow < m; endRow ++){
                // 接下来与求数组的连续子数组最大和相同
                int sumBetweenStartrowAndEndrow = 0;
                int tempMaxSum = calSum[endRow][0] - (startRow == -1? 0: calSum[startRow][0]);
                int tempMinSum = tempMaxSum;
                for(int col = 0; col < n; col++)
                    sumBetweenStartrowAndEndrow += calSum[endRow][col] - (startRow == -1? 0: calSum[startRow][col]);
                for(int col = 1; col < n; col ++){
                    // startRow=-1需要求取第一行在第col列的和,由于第一行没有前缀和做差,所以需要特殊处理
                    colSum[col] = startRow == -1? calSum[endRow][col]: calSum[endRow][col] - calSum[startRow][col];
                    tempMaxSum = Math.max(colSum[col], colSum[col] + tempMaxSum);
                    tempMinSum = Math.min(colSum[col], colSum[col] + tempMinSum);
                    maxSize = Math.max(sumBetweenStartrowAndEndrow - tempMinSum, Math.max(maxSize, tempMaxSum));
                }
            }
        }
        System.out.println(maxSize);
    }
}

发表于 2021-08-17 18:42:03 回复(0)