华为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在线答疑。