【限时免费】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.rhkb.cn/news/196211.html

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

相关文章

解析:什么是生成式AI?与其他类型的AI有何不同?

原创 | 文 BFT机器人 快速浏览一下头条新闻&#xff0c;你会发现生成式AI似乎无处不在。事实上&#xff0c;一些新闻标题甚至可能是通过生成式AI编写的&#xff0c;例如OpenAI旗下的ChatGPT&#xff0c;这个聊天机器人已经展现出了生成看起来像人类所写文本的惊人能力。 当人们…

Ubuntu18.04安装Loam保姆级教程

系统环境&#xff1a;Ubuntu18.04.6 LTS 1.Loam的安装前要求&#xff1a; 1.1 ROS安装&#xff1a;参考我的另一篇博客 Ubuntu18.04安装ROS-melodic保姆级教程_灬杨三岁灬的博客-CSDN博客还是那句话&#xff0c;有时候加了这行也不好使&#xff0c;我是疯狂试了20次&#xf…

用script去做前端html表格分页/排序

前言: 掘弃掉与后端交互做分页和互导,有利有弊吧; 在小数据的时候,如果不停来回朝服务端发送请求,会造成堵塞.于是,放弃了之前的前后端ajax方式去请求分页表格,使用script去弄一个,降低服务器的压力; 整体思路图: 代码构造: {% extends "order_header_same.html" …

stm32入门建议跳过固件库去学习hal库吗?

stm32入门建议跳过固件库去学习hal库吗? 如果要以单片机作为以后的工作方向&#xff0c;建议还是深入了解一下单片机的原理与机制&#xff0c;比如串口收发的时候&#xff0c;内部的寄存器是怎么工作的&#xff0c;中断又是怎么工作的&#xff0c;然后我们又是怎么进行中断处…

uniapp优化h5项目-摇树优化,gzip压缩和删除console.log

1.摇树优化 勾选摇树优化,打包删除死代码 2.gzip压缩和删除console.log 安装插件webpack和compression-webpack-plugin webpack插件 npm install webpack4.46.0 --save-devcompression-webpack-plugin插件 npm install compression-webpack-plugin6.1.1 --save-devconst Com…

代码随想录算法训练营第25天|216.组合总和III 17.电话号码的字母组合

JAVA代码编写 216. 组合总和III 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k …

【观察】华为:数智世界“一触即达”,应对数智化转型“千变万化”

毫无疑问&#xff0c;数智化既是这个时代前进所趋&#xff0c;也是国家战略所指&#xff0c;更是所有企业未来发展进程中达成的高度共识。 但也要看到&#xff0c;由于大量新兴技术的出现&#xff0c;技术热点不停的轮转&#xff0c;加上市场环境的快速变化&#xff0c;让数智化…

数据结构--栈与队列

目录 前言 1.栈 1.1栈的概念及结构 1.2接口函数 1.3函数实现 1.4如何使用 2.队列 2.1队列的概念及结构 2.2接口函数 2.3函数实现 2.4如何使用 前言 前面我们已经学习了顺序表和链表&#xff0c;今天我们来学习栈与队列&#xff0c;这两种结构也属于线性表&#xff0c;实…

顺序表(数据结构与算法)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

从0开始学习JavaScript--JavaScript 流程控制

JavaScript中的流程控制结构是编写结构化、可读性强的代码的关键。本文将深入研究JavaScript中的流程控制&#xff0c;包括条件语句、循环结构、跳转语句等&#xff0c;并通过丰富的示例代码来更全面地了解和运用这些概念。 条件语句 条件语句用于基于不同的条件执行不同的代…

架构开发与优化咨询和实施服务

服务概述 得益于硬件平台算力的提升&#xff0c;汽车电子电气架构的集成度逐渐提高&#xff0c;从单体ECU、到功能域集成控制器、到区域集成控制器&#xff0c;多域融合成为了目前行业中软件工程的重要工作内容。同时&#xff0c;在传统控制器C代码开发的基础上&#xff0c;C、…

C#中.NET 7.0 Windows窗体应用通过EF访问新建数据库

目录 一、 操作步骤 二、编写EF模型和数据库上下文 三、移植&#xff08;Migrations&#xff09;数据库 四、编写应用程序 五、生成效果 前文已经说过.NET Framework4.8 控制台应用通过EF访问已经建立的和新建的数据库。 前文已经说过.NET 6.0 控制台应用通过EF访问…

μC/OS-II---事件标志组管理1(os_flag.c)

目录 事件标志组创建事件标志组删除事件标志组获取/等待 当任务要与多个事件同步时&#xff0c;就要使用事件标志组。一个事件标志就是一个二值信号&#xff0c;事件标志组是若干二值信号的组合。使用事件标志组同步任务分为独立性同步和关联性同步。 事件标志组创建 flags&a…

MySql分区

一、什么是分区 MySQL分区是一种数据库设计和管理技术&#xff0c;它允许你将表分割成独立的、具有特定规则的存储单元。每个分区可以独立地进行管理&#xff0c;包括备份、恢复和优化。分区的主要目的是提高查询性能、简化维护以及实现数据的更有效管理。 以下是MySQL分区的…

IDEA 集成 Docker 插件一键部署 SpringBoot 应用

目录 前言IDEA 安装 Docker 插件配置 Docker 远程服务器编写 DockerFileSpringBoot 项目部署配置SpringBoot 项目部署结语 前言 随着容器化技术的崛起&#xff0c;Docker成为了现代软件开发的关键工具。在Java开发中&#xff0c;Spring Boot是一款备受青睐的框架&#xff0c;然…

PCL 半径滤波剔除噪点(二)

目录 一、算法原理二、注意事项三、代码实现一、算法原理 PCL半径滤波是删除在输入的点云一定范围内没有达到足够多领域的所有数据点。通俗的讲:就是以一个点p给定一个范围r,领域点要求的个数为m,r若在这个点的r范围内部的个数大于m则保留,小于m则删除。因此,使用该算法时…

阎良区公益创投之“小飞机大梦想” 航模DIY主题活动

创造是人类探索迈出的第一步&#xff0c;科学是开启奇妙世界的金钥匙。为进一步提升“未来星”对科技知识的兴趣&#xff0c;培养他们的科学创新精神&#xff0c;11月16日&#xff0c;阎良区社会组织公益创投——“未来星”助力乡村留守儿童成长计划项目在阎良区聚宝小学开展“…

【淘宝API】商品详情+搜索商品列表接口

淘宝商品详情API接口可以使用淘宝开放平台提供的SDK或API来获取。这些接口可以用于获取商品的详细信息&#xff0c;如标题、价格、描述、图片等。 以下是使用淘宝开放平台API获取商品详情的步骤&#xff1a; 注册淘宝开放平台账号&#xff0c;并创建应用&#xff0c;获取应用…

【具身智能评估1】具身视觉语言规划(EVLP)仿真环境汇总

参考论文&#xff1a;Core Challenges in Embodied Vision-Language Planning 论文作者&#xff1a;Jonathan Francis, Nariaki Kitamura, Felix Labelle, Xiaopeng Lu, Ingrid Navarro, Jean Oh 论文原文&#xff1a;https://arxiv.org/abs/2106.13948 论文出处&#xff1a;Jo…

C#学习相关系列之Linq常用方法---排序(一)

一、构建数据 public class Student_1{public int ID { get; set; }public string Name { get; set; }public int Chinese { get; set; }public int Math { get; set; }public int English { get; set; }public override string ToString(){return string.Format("ID:{0},…