
华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
给定一个二维整数矩阵,要在这个矩阵中选出一个子矩阵,使得这个子矩阵内所有的数字和尽量大。我们把这个子矩阵称为最大子矩阵,子矩阵的选取原则是原矩阵中一块相互连续的矩形区域。
二、输入描述
输入的第一行包含2个整数n, m (1 <= n, m <= 10),表示一个n行m列的矩阵,下面有n行,每行有m个整数,同一行中,每2个数字之间有1个空格,最后一个数字后面没有空格。所有的数字的在[-1000, 1000]之间。
三、输出描述
输出一行一个数字,表示选出的和最大的子矩阵内所有的数字和。## 四、测试用例
测试用例1:
1、输入
3 4
 -3 5 -1 5
 2 4 -2 4
 -1 3 -1 3
2、输出
20
3、说明
选出的和最大的子矩阵是后3列的所有行,其和为:
5 -1 5
 4 -2 4
 3 -1 3
和为:(5 + (-1) + 5 + 4 + (-2) + 4 + 3 + (-1) + 3) = 20
测试用例2:
1、输入
4 4
 -1 -1 -1 -1
 -1 2 2 -1
 -1 2 2 -1
 -1 -1 -1 -1
2、输出
8
3、说明
选出的和最大的子矩阵是中间2x2的矩阵,即:
2 2
 2 2
和为:(2 + 2 + 2 + 2) = 8
五、解题思路
1、问题理解
给定一个二维整数矩阵,要求选出一个矩形区域(子矩阵),使得其中的数字和尽可能大。这个问题可以看作是一个二维最大子数组和问题的扩展版本,类似于在一维数组中找到最大子数组和的问题。
2、卡德恩算法
卡德恩算法的核心思想是使用动态规划,通过维护一个局部最优解来逐步构造全局最优解,在一维数组中找到和最大的连续子数组。
一维数组中寻找最大子数组和的经典算法。时间复杂度为O(n),动态规划思想通过记录当前的最大和,逐步遍历数组并更新全局最大值。
 在二维矩阵中,可以通过固定上下边界,将二维问题转化为多个一维问题,并用Kadane’s算法在每个转换后的一维数组中求解最大子数组和。
3、具体步骤:
- 通过固定上下边界,将二维问题化简为多个一维问题。具体来说,对于每对上下边界top和bottom,计算边界内每一列的和,形成一个一维数组temp。
 - 然后对该一维数组temp使用Kadane’s算法,求出这一维数组中的最大子数组和,这相当于当前上下边界之间的最大子矩阵和。
 - 不断枚举所有可能的上下边界,并记录全局最大值。
 
4、时间复杂度
枚举上下边界的时间复杂度为O(n2),每次求解一维子数组和的时间复杂度为O(m),因此总时间复杂度为O(n2 * m)。由于n和m均不超过10,算法复杂度是可以接受的。
5、空间复杂度
需要一个大小为m的临时数组temp[]来存储每次上下边界之间的列和。除此之外,算法没有额外的空间开销,空间复杂度为O(m)。
六、Java算法源码
public class OdTest01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 读取矩阵的行数和列数
        int n = sc.nextInt(); // 行数
        int m = sc.nextInt(); // 列数
        int[][] matrix = new int[n][m];
        // 读取矩阵
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                matrix[i][j] = sc.nextInt();
            }
        }
        // 计算最大子矩阵的和
        System.out.println(maxSubmatrixSum(matrix, n, m));
    }
    // 使用Kadane's算法扩展版来求解最大子矩阵的和
    private static int maxSubmatrixSum(int[][] matrix, int n, int m) {
        int maxSum = Integer.MIN_VALUE;  // 初始化最大和为最小值
        // 临时数组,用来存储每一列的和
        int[] temp = new int[m];
        // 枚举每一对上下边界
        for (int top = 0; top < n; top++) {
            // 清空temp数组,重新计算
            for (int i = 0; i < m; i++) {
                temp[i] = 0;
            }
            // 枚举下边界
            for (int bottom = top; bottom < n; bottom++) {
                // 累加每一列的元素
                for (int i = 0; i < m; i++) {
                    temp[i] += matrix[bottom][i];
                }
                // 使用Kadane's算法找出一维数组的最大子数组和
                int currentMaxSum = kadane(temp, m);
                // 更新全局最大和
                maxSum = Math.max(maxSum, currentMaxSum);
            }
        }
        return maxSum;
    }
    // Kadane算法,求一维数组的最大子数组和
    private static int kadane(int[] array, int size) {
        int maxEndingHere = array[0];
        int maxSoFar = array[0];
        for (int i = 1; i < size; i++) {
            // 动态规划,比较当前元素和之前的最大和
            maxEndingHere = Math.max(array[i], maxEndingHere + array[i]);
            // 更新全局最大和
            maxSoFar = Math.max(maxSoFar, maxEndingHere);
        }
        return maxSoFar;
    }
}
 
七、效果展示
1、输入
3 3
 0 1 0
 1 1 1
 0 1 0
2、输出
5
3、说明
矩阵如下:
0 1 0
 1 1 1
 0 1 0
选出的和最大的子矩阵是整个矩阵,和为:0 + 1 + 0 + 1 + 1 + 1 + 0 + 1 + 0 = 5

🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 2024 E卷 200分)
🏆本文收录于,华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。




















