华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/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)。
六、Python算法源码
def max_submatrix_sum(matrix, n, m):
max_sum = float('-inf') # 初始化最大和为负无穷大
# 临时数组,用来存储每一列的和
temp = [0] * m
# 枚举每一对上下边界
for top in range(n):
# 清空temp数组,重新计算
temp = [0] * m
# 枚举下边界
for bottom in range(top, n):
# 累加每一列的元素
for i in range(m):
temp[i] += matrix[bottom][i]
# 使用Kadane's算法找出一维数组的最大子数组和
current_max_sum = kadane(temp, m)
# 更新全局最大和
max_sum = max(max_sum, current_max_sum)
return max_sum
# Kadane算法,求一维数组的最大子数组和
def kadane(array, size):
max_ending_here = array[0] # 当前最大和
max_so_far = array[0] # 全局最大和
for i in range(1, size):
# 动态规划,比较当前元素和之前的最大和
max_ending_here = max(array[i], max_ending_here + array[i])
# 更新全局最大和
max_so_far = max(max_so_far, max_ending_here)
return max_so_far
# 主函数,读取输入
if __name__ == "__main__":
n, m = map(int, input().split()) # 读取矩阵的行数和列数
matrix = [list(map(int, input().split())) for _ in range(n)] # 读取矩阵数据
# 输出最大子矩阵的和
print(max_submatrix_sum(matrix, n, m))
七、JavaScript算法源码
function maxSubmatrixSum(matrix, n, m) {
let maxSum = -Infinity; // 初始化最大和为负无穷大
// 临时数组,用来存储每一列的和
let temp = new Array(m).fill(0);
// 枚举每一对上下边界
for (let top = 0; top < n; top++) {
temp.fill(0); // 清空temp数组,重新计算
// 枚举下边界
for (let bottom = top; bottom < n; bottom++) {
// 累加每一列的元素
for (let i = 0; i < m; i++) {
temp[i] += matrix[bottom][i];
}
// 使用Kadane's算法找出一维数组的最大子数组和
let currentMaxSum = kadane(temp, m);
// 更新全局最大和
maxSum = Math.max(maxSum, currentMaxSum);
}
}
return maxSum;
}
// Kadane算法,求一维数组的最大子数组和
function kadane(array, size) {
let maxEndingHere = array[0]; // 当前最大和
let maxSoFar = array[0]; // 全局最大和
for (let i = 1; i < size; i++) {
// 动态规划,比较当前元素和之前的最大和
maxEndingHere = Math.max(array[i], maxEndingHere + array[i]);
// 更新全局最大和
maxSoFar = Math.max(maxSoFar, maxEndingHere);
}
return maxSoFar;
}
// 主函数,读取输入
function main() {
let input = prompt("输入行数和列数:").split(" "); // 读取矩阵的行数和列数
let n = parseInt(input[0]);
let m = parseInt(input[1]);
let matrix = [];
for (let i = 0; i < n; i++) {
matrix.push(prompt("输入矩阵行数据:").split(" ").map(Number)); // 读取矩阵数据
}
// 输出最大子矩阵的和
console.log(maxSubmatrixSum(matrix, n, m));
}
main();
八、C算法源码
#include <stdio.h>
#include <limits.h>
int kadane(int array[], int size) {
// Kadane算法,求一维数组的最大子数组和
int maxEndingHere = array[0]; // 当前最大和
int maxSoFar = array[0]; // 全局最大和
for (int i = 1; i < size; i++) {
// 动态规划,比较当前元素和之前的最大和
maxEndingHere = (array[i] > maxEndingHere + array[i]) ? array[i] : maxEndingHere + array[i];
// 更新全局最大和
maxSoFar = (maxSoFar > maxEndingHere) ? maxSoFar : maxEndingHere;
}
return maxSoFar;
}
int maxSubmatrixSum(int matrix[][10], int n, int m) {
int maxSum = INT_MIN; // 初始化最大和为最小值
// 临时数组,用来存储每一列的和
int temp[10] = {0};
// 枚举每一对上下边界
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);
// 更新全局最大和
if (currentMaxSum > maxSum) {
maxSum = currentMaxSum;
}
}
}
return maxSum;
}
int main() {
int n, m;
scanf("%d %d", &n, &m); // 读取矩阵的行数和列数
int matrix[10][10]; // 假设最大为10x10的矩阵
// 读取矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &matrix[i][j]);
}
}
// 输出最大子矩阵的和
printf("%d\n", maxSubmatrixSum(matrix, n, m));
return 0;
}
九、C++算法源码
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
// Kadane算法,求一维数组的最大子数组和
int kadane(vector<int>& array, int size) {
int maxEndingHere = array[0]; // 当前最大和
int maxSoFar = array[0]; // 全局最大和
for (int i = 1; i < size; i++) {
// 动态规划,比较当前元素和之前的最大和
maxEndingHere = max(array[i], maxEndingHere + array[i]);
// 更新全局最大和
maxSoFar = max(maxSoFar, maxEndingHere);
}
return maxSoFar;
}
// 使用Kadane's算法扩展版来求解最大子矩阵的和
int maxSubmatrixSum(vector<vector<int>>& matrix, int n, int m) {
int maxSum = INT_MIN; // 初始化最大和为最小值
// 临时数组,用来存储每一列的和
vector<int> temp(m);
// 枚举每一对上下边界
for (int top = 0; top < n; top++) {
fill(temp.begin(), temp.end(), 0); // 清空temp数组,重新计算
// 枚举下边界
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 = max(maxSum, currentMaxSum);
}
}
return maxSum;
}
int main() {
int n, m;
cin >> n >> m; // 读取矩阵的行数和列数
vector<vector<int>> matrix(n, vector<int>(m));
// 读取矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> matrix[i][j];
}
}
// 输出最大子矩阵的和
cout << maxSubmatrixSum(matrix, n, m) << endl;
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2024 E卷 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。