当前位置: 首页 > news >正文

【华为OD】最大子矩阵和

【华为OD】最大子矩阵和

题目描述

给定一个二维整数矩阵,要在这个矩阵中选出一个子矩阵,使得这个子矩阵内所有的数字和尽量大。我们把这个子矩阵称为"和最大子矩阵",子矩阵的选取原则,是原矩阵中一段相互连续的矩形区域。

输入描述

  • 输入的第一行包含两个整数 N,M (1 <= N, M <= 10)
  • 表示一个 N 行 M 列的矩阵
  • 下面有 N 行,每行有 M 个整数
  • 同一行中每两个数字之间有一个空格
  • 最后一个数字后面没有空格
  • 所有的数字得在 -1000 ~ 1000 之间

输出描述

输出一行,一个数字。表示选出的"和最大子矩阵"内所有数字的和

示例

输入:

3 4
-3 5 -1 5
2 4 -2 4
-1 3 -1 3

输出:

20

说明:
一个3*4的矩阵中后面3列的和为20,和最大

解题思路

这是一个经典的最大子矩阵和问题,是最大子数组和问题在二维空间的扩展。

核心思想:

  1. 枚举所有可能的上下边界
  2. 对于每一对上下边界,将问题转化为一维的最大子数组和问题
  3. 使用Kadane算法求解一维最大子数组和

我将提供两种解法:暴力枚举法优化的Kadane算法

解法一:暴力枚举法

暴力枚举所有可能的子矩阵,计算每个子矩阵的和,找出最大值。

Java实现

import java.util.*;public class Solution1 {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();}}int maxSum = Integer.MIN_VALUE;// 枚举所有可能的子矩阵for (int r1 = 0; r1 < n; r1++) {for (int c1 = 0; c1 < m; c1++) {for (int r2 = r1; r2 < n; r2++) {for (int c2 = c1; c2 < m; c2++) {// 计算子矩阵 (r1,c1) 到 (r2,c2) 的和int sum = 0;for (int i = r1; i <= r2; i++) {for (int j = c1; j <= c2; j++) {sum += matrix[i][j];}}maxSum = Math.max(maxSum, sum);}}}}System.out.println(maxSum);}
}

Python实现

def solve_brute_force():n, m = map(int, input().split())matrix = []for _ in range(n):row = list(map(int, input().split()))matrix.append(row)max_sum = float('-inf')# 枚举所有可能的子矩阵for r1 in range(n):for c1 in range(m):for r2 in range(r1, n):for c2 in range(c1, m):# 计算子矩阵 (r1,c1) 到 (r2,c2) 的和current_sum = 0for i in range(r1, r2 + 1):for j in range(c1, c2 + 1):current_sum += matrix[i][j]max_sum = max(max_sum, current_sum)print(max_sum)solve_brute_force()

C++实现

#include <iostream>
#include <vector>
#include <climits>
using namespace std;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];}}int maxSum = INT_MIN;// 枚举所有可能的子矩阵for (int r1 = 0; r1 < n; r1++) {for (int c1 = 0; c1 < m; c1++) {for (int r2 = r1; r2 < n; r2++) {for (int c2 = c1; c2 < m; c2++) {// 计算子矩阵 (r1,c1) 到 (r2,c2) 的和int sum = 0;for (int i = r1; i <= r2; i++) {for (int j = c1; j <= c2; j++) {sum += matrix[i][j];}}maxSum = max(maxSum, sum);}}}}cout << maxSum << endl;return 0;
}

解法二:Kadane算法优化

通过固定上下边界,将二维问题转化为一维最大子数组和问题,使用Kadane算法求解。

Java实现

import java.util.*;public class Solution2 {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();}}int maxSum = Integer.MIN_VALUE;// 枚举所有可能的上下边界for (int top = 0; top < n; top++) {int[] temp = new int[m];for (int bottom = top; bottom < n; bottom++) {// 将第bottom行加到temp数组中for (int j = 0; j < m; j++) {temp[j] += matrix[bottom][j];}// 对temp数组使用Kadane算法找最大子数组和int currentMax = kadane(temp);maxSum = Math.max(maxSum, currentMax);}}System.out.println(maxSum);}// Kadane算法求最大子数组和private static int kadane(int[] arr) {int maxSoFar = arr[0];int maxEndingHere = arr[0];for (int i = 1; i < arr.length; i++) {maxEndingHere = Math.max(arr[i], maxEndingHere + arr[i]);maxSoFar = Math.max(maxSoFar, maxEndingHere);}return maxSoFar;}
}

Python实现

def kadane(arr):"""Kadane算法求最大子数组和"""max_so_far = arr[0]max_ending_here = arr[0]for i in range(1, len(arr)):max_ending_here = max(arr[i], max_ending_here + arr[i])max_so_far = max(max_so_far, max_ending_here)return max_so_fardef solve_kadane():n, m = map(int, input().split())matrix = []for _ in range(n):row = list(map(int, input().split()))matrix.append(row)max_sum = float('-inf')# 枚举所有可能的上下边界for top in range(n):temp = [0] * mfor bottom in range(top, n):# 将第bottom行加到temp数组中for j in range(m):temp[j] += matrix[bottom][j]# 对temp数组使用Kadane算法找最大子数组和current_max = kadane(temp)max_sum = max(max_sum, current_max)print(max_sum)solve_kadane()

C++实现

#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;// Kadane算法求最大子数组和
int kadane(vector<int>& arr) {int maxSoFar = arr[0];int maxEndingHere = arr[0];for (int i = 1; i < arr.size(); i++) {maxEndingHere = max(arr[i], maxEndingHere + arr[i]);maxSoFar = max(maxSoFar, maxEndingHere);}return maxSoFar;
}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];}}int maxSum = INT_MIN;// 枚举所有可能的上下边界for (int top = 0; top < n; top++) {vector<int> temp(m, 0);for (int bottom = top; bottom < n; bottom++) {// 将第bottom行加到temp数组中for (int j = 0; j < m; j++) {temp[j] += matrix[bottom][j];}// 对temp数组使用Kadane算法找最大子数组和int currentMax = kadane(temp);maxSum = max(maxSum, currentMax);}}cout << maxSum << endl;return 0;
}

解法三:前缀和优化(额外解法)

使用二维前缀和来快速计算任意子矩阵的和。

Java实现

import java.util.*;public class Solution3 {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();}}// 构建前缀和数组int[][] prefixSum = new int[n + 1][m + 1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {prefixSum[i][j] = matrix[i-1][j-1] + prefixSum[i-1][j] + prefixSum[i][j-1] - prefixSum[i-1][j-1];}}int maxSum = Integer.MIN_VALUE;// 枚举所有可能的子矩阵for (int r1 = 0; r1 < n; r1++) {for (int c1 = 0; c1 < m; c1++) {for (int r2 = r1; r2 < n; r2++) {for (int c2 = c1; c2 < m; c2++) {// 使用前缀和快速计算子矩阵和int sum = prefixSum[r2+1][c2+1] - prefixSum[r1][c2+1] - prefixSum[r2+1][c1] + prefixSum[r1][c1];maxSum = Math.max(maxSum, sum);}}}}System.out.println(maxSum);}
}

Python实现

def solve_prefix_sum():n, m = map(int, input().split())matrix = []for _ in range(n):row = list(map(int, input().split()))matrix.append(row)# 构建前缀和数组prefix_sum = [[0] * (m + 1) for _ in range(n + 1)]for i in range(1, n + 1):for j in range(1, m + 1):prefix_sum[i][j] = (matrix[i-1][j-1] + prefix_sum[i-1][j] + prefix_sum[i][j-1] - prefix_sum[i-1][j-1])max_sum = float('-inf')# 枚举所有可能的子矩阵for r1 in range(n):for c1 in range(m):for r2 in range(r1, n):for c2 in range(c1, m):# 使用前缀和快速计算子矩阵和current_sum = (prefix_sum[r2+1][c2+1] - prefix_sum[r1][c2+1] - prefix_sum[r2+1][c1] + prefix_sum[r1][c1])max_sum = max(max_sum, current_sum)print(max_sum)solve_prefix_sum()

C++实现

#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;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];}}// 构建前缀和数组vector<vector<int>> prefixSum(n + 1, vector<int>(m + 1, 0));for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {prefixSum[i][j] = matrix[i-1][j-1] + prefixSum[i-1][j] + prefixSum[i][j-1] - prefixSum[i-1][j-1];}}int maxSum = INT_MIN;// 枚举所有可能的子矩阵for (int r1 = 0; r1 < n; r1++) {for (int c1 = 0; c1 < m; c1++) {for (int r2 = r1; r2 < n; r2++) {for (int c2 = c1; c2 < m; c2++) {// 使用前缀和快速计算子矩阵和int sum = prefixSum[r2+1][c2+1] - prefixSum[r1][c2+1] - prefixSum[r2+1][c1] + prefixSum[r1][c1];maxSum = max(maxSum, sum);}}}}cout << maxSum << endl;return 0;
}

算法复杂度分析

解法一:暴力枚举法

  • 时间复杂度:O(N²M²(N+M)),六重循环
  • 空间复杂度:O(1)

解法二:Kadane算法优化

  • 时间复杂度:O(N²M),三重循环
  • 空间复杂度:O(M)

解法三:前缀和优化

  • 时间复杂度:O(N²M²),四重循环但每次计算子矩阵和为O(1)
  • 空间复杂度:O(NM)

示例分析

对于给定的示例:

3 4
-3  5 -1  52  4 -2  4
-1  3 -1  3

最大子矩阵是后面3列的所有元素:

 5 -1  5    (第1行后3列)4 -2  4    (第2行后3列)3 -1  3    (第3行后3列)

和为:5 + (-1) + 5 + 4 + (-2) + 4 + 3 + (-1) + 3 = 20

总结

三种解法各有特点:

  1. 暴力枚举法:思路简单直观,但时间复杂度较高
  2. Kadane算法优化:是最优解法,时间复杂度最低,推荐使用
  3. 前缀和优化:在某些场景下有优势,特别是需要多次查询时

对于这道题目,由于N, M ≤ 10,数据规模较小,三种方法都能通过。但在实际应用中,Kadane算法优化是最佳选择,它将二维问题巧妙地转化为一维问题,大大降低了时间复杂度。


文章转载自:

http://lKAr9O0E.cptzd.cn
http://UIi7L6O3.cptzd.cn
http://M5vsxPoy.cptzd.cn
http://mQDMMcuo.cptzd.cn
http://OaCOEUOE.cptzd.cn
http://xjTohzbt.cptzd.cn
http://xBeyV1tR.cptzd.cn
http://dGSRmfXY.cptzd.cn
http://wSa7AyP6.cptzd.cn
http://cUMaPpPR.cptzd.cn
http://axpAErao.cptzd.cn
http://0YeOUHpG.cptzd.cn
http://SGIUTbkv.cptzd.cn
http://y0MZReKT.cptzd.cn
http://NYSxq6pY.cptzd.cn
http://veGI9tvx.cptzd.cn
http://25pzVjI1.cptzd.cn
http://riJ3Ygoe.cptzd.cn
http://V64lGDDM.cptzd.cn
http://C0D5fe9V.cptzd.cn
http://3cMMcKPn.cptzd.cn
http://0Sy318n1.cptzd.cn
http://HtTpAbG2.cptzd.cn
http://zvAZcNX4.cptzd.cn
http://deCzIqaY.cptzd.cn
http://weFFMEl4.cptzd.cn
http://nHDcx7rI.cptzd.cn
http://aIGVOqGY.cptzd.cn
http://5CzaCdUz.cptzd.cn
http://CGODGpB6.cptzd.cn
http://www.dtcms.com/a/375396.html

相关文章:

  • 课前准备--空间转录组联合GWAS进行数据分析(gsMap)
  • RPC 与http对比
  • OpenEuler安装gitlab,部署gitlab-runner
  • 电池热管理新突破!《Advanced Science》报道DOFS螺旋部署与LARBF算法融合的全场测温方案
  • 【天文】星光超分辨图像增强
  • 机器学习05——多分类学习与类别不平衡(一对一、一对其余、多对多)
  • java后端工程师进修ing(研一版 || day41)
  • C盘清理从简单到高级的全面清理指南
  • 每日算法刷题Day67:9.9:leetcode bfs10道题,用时2h30min
  • PCL 基于法向量进行颜色插值赋色
  • 四数之和
  • MySql案例详解之事务
  • golang 语言核心
  • 【项目】在AUTODL上使用langchain实现《红楼梦》知识图谱和RAG混合检索(二)RAG部分
  • 安卓学习 之 贞布局FrameLayout
  • 【ISP】Charlite工具实操
  • IntelliJ IDEA断点调试全攻略
  • OceanBase存储过程基本使用
  • 使用 OBD 交互式部署单点OceanBase数据库
  • 内存管理这一块
  • 【深度学习新浪潮】什么是具身智能?
  • Linux tc 常用命令总结(网卡限速、延迟、丢包与整形)
  • Windows 命令行:路径末端的反斜杠
  • Shell脚本编程基本认识
  • Redis 面试
  • 大学地理信息科学该如何学习才能好就业
  • 浅谈“SVMSPro视频切片”技术应用场景
  • OpenHarmony多模输入子系统全链路剖析:从HCS配置到HDI芯片驱动源码深度解读
  • 1. linux 下qt 应用开机自启,需要sudo时
  • QML中的Popup