【限时免费】20天拿下华为OD笔试之 【前缀和】2023B-最大子矩阵和【欧弟算法】全网注释最详细分类最全的华为OD真题题解

文章目录

  • 题目描述与示例
    • 题目描述
    • 输入描述
    • 输出描述
    • 示例
      • 输入
      • 输出
      • 说明
  • 解题思路
    • 如何表示一个子矩阵
    • 暴力解法
    • 二维前缀和优化
    • 二维前缀和矩阵的构建
  • 代码
    • 解法一:二维前缀和
      • Python
      • Java
      • C++
      • 时空复杂度
    • 解法二:暴力解法(不推荐)
      • Python
      • Java
      • C++
      • 时空复杂度
  • 华为OD算法/大厂面试高频题算法练习冲刺训练

题目描述与示例

题目描述

给定一个二维整数矩阵,要在这个矩阵中。选出一个子矩阵,使得这个子矩阵内所有的数字和尽量大

我们把这个子矩阵称为 “和最大子矩阵”,子矩阵的选取原则,是原矩阵中一段相互连续的矩形区域

输入描述

输入的第一行包含两个整数N,M

(1 <= N, M <= 10)

表示一个 N 行 M 列的矩阵

下面有N行 每行有M个整数

同一行中每两个数字之间有一个空格

最后一个数字后面没有空格

所有的数字得在-1000 ~ 1000之间

输出描述

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

示例

输入

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

输出

20

说明

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

解题思路

如何表示一个子矩阵

一个子矩阵可以由四个参数决定,分别为上底、下底、左宽、右宽,分别用变量abcd表示的话,如下图中灰色区域为通过四个参数所确定的矩形。

如果我们想要枚举所有子矩阵,只需要分别枚举abcd,写一个4层嵌套的for循环即可。

for a in range(n):for b in range(a, n):for c in range(m):for d in range(c, m):pass

暴力解法

暴力解法是很容易想到的,我们只需要枚举所有的子矩阵,然后对每一个子矩阵进行矩阵内所有元素求和即可。其核心代码为

for a in range(n):for b in range(a, n):for c in range(m):for d in range(c, m):submat_sum = 0for i in range(a, b+1):for j in range(c, d+1):submat_sum += mat[i][j]ans = max(submat_sum, ans)

注意到会出现6for循环嵌套,时间复杂度为 O ( n 3 m 3 ) O(n^3m^3) O(n3m3)。由于数据范围为(1 <= n, m <= 10),故取最大值时复杂度约为 O ( 1 0 6 ) O(10^6) O(106),无法通过全部用例,故应该思考如何优化。

二维前缀和优化

注意,该方法和LeetCode 304、二维区域和检索 - 矩阵不可变 是类似的。

注意到每一个子矩阵的计算都可以用以下方式进行拆解。

拆解后的四个区域具有一个共同的特点:它们的上底均为上边界、左宽均为左边界

因此需要考虑类似一维前缀和的方法,将所有的上底为上边界、左宽为左边界(即a = 0c = 0)的子矩阵的和提前记录在二维前缀和矩阵pre_sum_mat中。

pre_sum_mat是一个大小为(n+1)*(m+1)的矩阵,pre_sum_mat[i][j]表示以第0行、第0列为开头(取得到的开区间),第i行、第j列为结尾(取不到的闭区间)的子矩阵的和。

上述的四个区域的和,就可以分别使用pre_sum_mat[b+1][d+1]pre_sum_mat[b+1][c]pre_sum_mat[a][d+1]pre_sum_mat[a][c]来表示了。

这里对开/闭区间的理解是非常重要的,如果想不清楚的话,后面的代码很容易出错。如果把子矩阵用一种类似切片的方法表示(并不严谨的写法)为mat[a:b+1][c:d+1]。那么上述的分析过程可以写为

sum(mat[a:b+1][c:d+1])
= sum(mat[:b+1][:d+1]) + sum(mat[:a][:c]) - sum(mat[:b+1][:c]) - sum(mat[:a][:d+1])
= pre_sum_mat[b+1][d+1] + pre_sum_mat[a][c] - pre_sum_mat[b+1][c] - pre_sum_mat[a][d+1]

那么,在原矩阵mat中,分别以abcd为上底、下底、左宽、右宽的子矩阵的和,就可以记为

submat_sum = (pre_sum_mat[b+1][d+1] + pre_sum_mat[a][c] -pre_sum_mat[b+1][c] - pre_sum_mat[a][d+1])

上述计算的时间复杂度为O(1),因此这种做法规避了暴力解对子矩阵求和时出现的反复计算,降低了最内层求和时时间复杂度。如果把外部的循环体加上,代码为

for a in range(n):for b in range(a, n):for c in range(m):for d in range(c, m):submat_sum = pre_sum_mat[b+1][d+1] + pre_sum_mat[a][c] - \pre_sum_mat[b+1][c] - pre_sum_mat[a][d+1]ans = max(submat_sum, ans)

如果不想让最内层的索引出现+1,则可以修改for循环的范围,代码变为

for a in range(n):for b in range(a+1, n+1):for c in range(m):for d in range(c+1, m+1):submat_sum = pre_sum_mat[b][d] + pre_sum_mat[a][c] - \pre_sum_mat[b][c] - pre_sum_mat[a][d]ans = max(submat_sum, ans)

上述过程的时间复杂度为 O ( n 2 m 2 ) O(n^2m^2) O(n2m2)。当nm取最大值时复杂度约为 O ( 1 0 4 ) O(10^4) O(104),可以通过全部用例。

二维前缀和矩阵的构建

二维前缀和矩阵pre_sum_mat的构建也要用到类似上述的拆分过程,其核心代码如下

pre_sum_mat = [[0] * (m+1) for _ in range(n+1)]
for i in range(1, n+1):for j in range(1, m+1):pre_sum_mat[i][j] = pre_sum_mat[i-1][j] + pre_sum_mat[i][j-1] - \pre_sum_mat[i-1][j-1] + mat[i-1][j-1]

要特别注意二维前缀和pre_sum_mat的大小,在两个维度上均比原矩阵矩阵mat1

该过程的时间复杂度为 O ( n m ) O(nm) O(nm)

代码

解法一:二维前缀和

Python

# 题目:2023B-最大子矩阵和
# 分值:200
# 作者:闭着眼睛学数理化
# 算法:二维前缀和
# 代码有看不懂的地方请直接在群上提问from math import infn, m = map(int, input().split())
mat = list()
for _ in range(n):row = list(map(int, input().split()))mat.append(row)# 构建二维前缀和数组
pre_sum_mat = [[0] * (m+1) for _ in range(n+1)]
for i in range(1, n+1):for j in range(1, m+1):pre_sum_mat[i][j] = pre_sum_mat[i-1][j] + pre_sum_mat[i][j-1] - \pre_sum_mat[i-1][j-1] + mat[i-1][j-1]# 初始化答案为负无穷小
ans = -inf# 枚举上底a
for a in range(n):# 枚举下底bfor b in range(a, n):# 枚举左宽cfor c in range(m):# 枚举右宽dfor d in range(c, m):# 此时四个参数能够表示一个子矩阵# 根据式子计算子矩阵和,更新anssubmat_sum = pre_sum_mat[b+1][d+1] + pre_sum_mat[a][c] - \pre_sum_mat[b+1][c] - pre_sum_mat[a][d+1]ans = max(submat_sum, ans)print(ans)

Java

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int m = scanner.nextInt();int[][] mat = new int[n][m];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {mat[i][j] = scanner.nextInt();}}int[][] preSumMat = new int[n + 1][m + 1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {preSumMat[i][j] = preSumMat[i - 1][j] + preSumMat[i][j - 1] - preSumMat[i - 1][j - 1] + mat[i - 1][j - 1];}}int ans = Integer.MIN_VALUE;for (int a = 0; a < n; a++) {for (int b = a; b < n; b++) {for (int c = 0; c < m; c++) {for (int d = c; d < m; d++) {int submatSum = preSumMat[b + 1][d + 1] + preSumMat[a][c] - preSumMat[b + 1][c] - preSumMat[a][d + 1];ans = Math.max(submatSum, ans);}}}}System.out.println(ans);}
}

C++

#include <iostream>
#include <vector>
#include <limits>using namespace std;int main() {int n, m;cin >> n >> m;vector<vector<int>> mat(n, vector<int>(m));for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {cin >> mat[i][j];}}vector<vector<int>> pre_sum_mat(n + 1, vector<int>(m + 1, 0));for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {pre_sum_mat[i][j] = pre_sum_mat[i - 1][j] + pre_sum_mat[i][j - 1] - pre_sum_mat[i - 1][j - 1] + mat[i - 1][j - 1];}}int ans = numeric_limits<int>::min();for (int a = 0; a < n; a++) {for (int b = a; b < n; b++) {for (int c = 0; c < m; c++) {for (int d = c; d < m; d++) {int submat_sum = pre_sum_mat[b + 1][d + 1] + pre_sum_mat[a][c] - pre_sum_mat[b + 1][c] - pre_sum_mat[a][d + 1];ans = max(submat_sum, ans);}}}}cout << ans << endl;return 0;
}

时空复杂度

时间复杂度: O ( n 2 m 2 ) O(n^2m^2) O(n2m2)

空间复杂度: O ( n m ) O(nm) O(nm)。二维前缀和矩阵所占空间。

解法二:暴力解法(不推荐)

Python

# 题目:2023B-最大子矩阵和
# 分值:200
# 作者:闭着眼睛学数理化
# 算法:暴力解
# 代码有看不懂的地方请直接在群上提问from math import infn, m = map(int, input().split())
mat = list()
for _ in range(n):row = list(map(int, input().split()))mat.append(row)# 初始化答案为负无穷小
ans = -inffor a in range(n):for b in range(a, n):for c in range(m):for d in range(c, m):submat_sum = 0for i in range(a, b+1):for j in range(c, d+1):submat_sum += mat[i][j]ans = max(submat_sum, ans)print(ans)

Java

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int m = scanner.nextInt();int[][] mat = new int[n][m];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {mat[i][j] = scanner.nextInt();}}int ans = Integer.MIN_VALUE;for (int a = 0; a < n; a++) {for (int b = a; b < n; b++) {for (int c = 0; c < m; c++) {for (int d = c; d < m; d++) {int submatSum = 0;for (int i = a; i <= b; i++) {for (int j = c; j <= d; j++) {submatSum += mat[i][j];}}ans = Math.max(submatSum, ans);}}}}System.out.println(ans);}
}

C++

#include <iostream>
#include <vector>
#include <climits>using namespace std;int main() {int n, m;cin >> n >> m;vector<vector<int>> mat(n, vector<int>(m));for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {cin >> mat[i][j];}}int ans = INT_MIN;for (int a = 0; a < n; a++) {for (int b = a; b < n; b++) {for (int c = 0; c < m; c++) {for (int d = c; d < m; d++) {int submatSum = 0;for (int i = a; i <= b; i++) {for (int j = c; j <= d; j++) {submatSum += mat[i][j];}}ans = max(submatSum, ans);}}}}cout << ans << endl;return 0;
}

时空复杂度

时间复杂度: O ( n 3 m 3 ) O(n^3m^3) O(n3m3)

空间复杂度: O ( 1 ) O(1) O(1)


华为OD算法/大厂面试高频题算法练习冲刺训练

  • 华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!

  • 课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

  • 每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!

  • 60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁

  • 可上全网独家的欧弟OJ系统练习华子OD、大厂真题

  • 可查看链接 大厂真题汇总 & OD真题汇总(持续更新)

  • 绿色聊天软件戳 od1336了解更多

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/190371.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win10关机设置里没有睡眠选项的解决方法

用户想给自己的Win10电脑设置睡眠模式&#xff0c;但是在关机设置里面找不到睡眠选项&#xff0c;导致自己不能顺利完成睡眠模式的设置。接下来小编给大家带来解决Win10关机设置里没有睡眠选项的简单方法&#xff0c;解决后用户就可以看到Win10电脑关机设置中有睡眠选项了。 Wi…

【汇编】Loop指令、段前缀

文章目录 前言一、Loop指令1.1 Loop指令是什么&#xff1f;1.2 他的条件是什么&#xff1f;1.3 例子示例1示例2 1.4 要点总结 二、段前缀2.1 为什么要引入他2.2 对策 总结 前言 在计算机编程的世界里&#xff0c;了解底层的硬件操作是提升程序员能力的关键一步。汇编语言作为一…

Pattern Recognition投稿经验

文章目录 ManuscriptTitle PageHighlightsAuthor BiographyDeclarationSubmit 合作推广&#xff0c;分享一个人工智能学习网站。计划系统性学习的同学可以了解下&#xff0c;点击助力博主脱贫( •̀ ω •́ )✧ 停更了大半年&#xff0c;近期终于完成了论文投稿&#xff0c;趁…

恕我直言,大模型对齐可能无法解决安全问题,我们都被表象误导了

是否听说过“伪对齐”这一概念&#xff1f; 在大型语言模型&#xff08;LLM&#xff09;的评估中&#xff0c;研究者发现了一个引人注目的现象&#xff1a;当面对多项选择题和开放式问题时&#xff0c;模型的表现存在显著差异。这一差异根源在于模型对复杂概念的理解不够全面&…

AI技术:分享8个非常实用的AI绘画网站

目录 1、Midjourney 2、Stable Diffusion Omline 3、Microsoft Designer 4、Craiyon 5、NightCafe Studio 6、Wombo 7、Dalle-2 8、Avatar AI 1、Midjourney 特点&#xff1a;业内标杆&#xff0c;效果最强大 Midjourney是基于diffusion的AI图画艺术生成器。生成图片不…

Redis维护缓存的方案选择

Redis中间件常常被用作缓存&#xff0c;而当使用了缓存的时候&#xff0c;缓存中数据的维护&#xff0c;往往是需要重点关注的&#xff0c;尤其是重点考虑的是数据一致性问题。以下是维护数据库缓存的一些常用方案。 1、先删除缓存&#xff0c;再更新数据库 导致数据不一致的…

VS2022 配置 OpenCV并开始第一个程序

VS2022安装 首先下载 VisualStudioSetup.exe 下载连接&#xff1a;Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 点击上面的链接即可进入到下载页面。进入到下载页面&#xff0c;可看到有几个版本可选&#xff0c;如下&#xff1a; 我选择的是企业版&#xff1a;E…

RT-DETR算法优化改进:Inner-IoU基于辅助边框的IoU损失,高效结合 GIoU, DIoU, CIoU,SIoU 等 | 2023.11

本文改进:Inner-IoU引入尺度因子 ratio 控制辅助边框的尺度大小用于计算损失,并与现有的基于 IoU ( GIoU, DIoU, CIoU,SIoU )损失进行有效结合 推荐指数:5颗星 新颖指数:5颗星 RT-DETR魔术师专栏介绍: https://blog.csdn.net/m0_63774211/category_12497375…

java+ 如何动态配置业务规则组

思路 1. 实现在页面上的动态配置规则组&#xff08;2张数据表枚举类serviceimplaction&#xff09; 2. 从数据库中表staffmoverules&#xff08;规则明细表&#xff09;或者staffmovetyperule&#xff08;规则组表&#xff09; &#xff0c;根据传入类型&#xff0c;取出规则编…

基于热交换算法优化概率神经网络PNN的分类预测 - 附代码

基于热交换算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于热交换算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于热交换优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…

苍穹外卖--员工分页查询

请求参数封装&#xff1a; Data public class EmployeePageQueryDTO implements Serializable {//员工姓名private String name;//页码private int page;//每页显示记录数private int pageSize;}请求结果封装&#xff1a; public class PageResult implements Serializable {…

C++各种字符转换

C各种字符转换 一.如何将char数组转化为string类型二. string转char数组&#xff1a;参考 一.如何将char数组转化为string类型 在C中&#xff0c;可以使用string的构造函数或者赋值操作符来将char数组转换为string类型。 方法1&#xff1a;使用string的构造函数 const char* c…