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

news2024/11/16 1:32:27

文章目录

  • 题目描述与示例
    • 题目描述
    • 输入描述
    • 输出描述
    • 示例
      • 输入
      • 输出
      • 说明
  • 解题思路
    • 如何表示一个子矩阵
    • 暴力解法
    • 二维前缀和优化
    • 二维前缀和矩阵的构建
  • 代码
    • 解法一:二维前缀和
      • Python
      • Java
      • C++
      • 时空复杂度
    • 解法二:暴力解法(不推荐)
      • Python
      • Java
      • C++
      • 时空复杂度
  • 华为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,和最大

解题思路

如何表示一个子矩阵

一个子矩阵可以由四个参数决定,分别为上底、下底、左宽、右宽,分别用变量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 = 0
                 for 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 inf


n, 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):
    # 枚举下底b
    for b in range(a, n):
        # 枚举左宽c
        for c in range(m):
            # 枚举右宽d
            for d in range(c, m):
                # 此时四个参数能够表示一个子矩阵
                # 根据式子计算子矩阵和,更新ans
                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)

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 inf


n, m = map(int, input().split())
mat = list()
for _ in range(n):
    row = list(map(int, input().split()))
    mat.append(row)


# 初始化答案为负无穷小
ans = -inf

for a in range(n):
     for b in range(a, n):
         for c in range(m):
             for d in range(c, m):
                 submat_sum = 0
                 for 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.coloradmin.cn/o/1223072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pattern Recognition投稿经验

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

聚观早报 |联想集团Q2财季业绩;小鹏汽车Q3营收

【聚观365】11月17日消息 联想集团Q2财季业绩 小鹏汽车Q3营收 微软发布两款自研AI芯片 FAA批准SpaceX再次发射星际飞船 2023 OPPO开发者大会 联想集团Q2财季业绩 全球数字经济领导企业联想集团公布截至2023年9月30日的2023/24财年第二财季业绩&#xff1a;整体营收达到10…

闲聊从零开发一个2D数字人流程实战

.2D数字人技术 百度&#xff0c;腾讯&#xff0c;等大厂都有自己的数字平台制作&#xff08;套壳&#xff1a;api后台转发vue前端&#xff09;&#xff0c;国外也有出名的heygen&#xff08;非常厉害一个&#xff09;通过开源项目组合实现&#xff0c;再打通每个项目已api的形…

OpenAI 变天:Sam Altman 被踢出局,原 CTO 暂代临时 CEO

文章目录 灵魂人物 Sam Altman 离任 OpenAICEO 下台&#xff1a;OpenAI 也宫斗&#xff1f;个人简介 hello&#xff0c;大家好&#xff0c;我是 Lorin&#xff0c;一觉醒来科技圈发生了一件令人震惊的大事&#xff1a;Sam Altman 被踢出局&#xff0c;原 CTO 暂代临时 CEO。 灵…

Flask学习一:概述

搭建项目 安装框架 pip install Flask第一个程序 from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return "Hello World"if __name__ __main__:app.run()怎么说呢&#xff0c;感觉还不错的样子。 调试模式 if __name__ __main__:a…

搭建企业社区,如何激发员工互动?

本文是关于企业内部社区搭建后怎么运营&#xff0c;如何激发员工互动。 作为运营者&#xff0c;我们搭建企业内部员工的目的首先得明确下来&#xff0c;一般都是打造和宣扬企业内部文化&#xff0c;发布公司政策通知和行业动态、组织公司关键节点活动、以及员工经验分享资源分…

【数据结构】图的存储结构及实现(邻接表和十字链表)

一.邻接矩阵的空间复杂度 假设图G有n个顶点e条边&#xff0c;则存储该图需要O&#xff08;n^2) 不适用稀疏图的存储 二.邻接表 1.邻接表的存储思想&#xff1a; 对于图的每个顶点vi&#xff0c;将所有邻接于vi的顶点链成一个单链表&#xff0c;称为顶点vi的边表&#xff08…

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

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

2023.11.18 Hadoop之 YARN

1.简介 Apache Hadoop YARN &#xff08;Yet Another Resource Negotiator&#xff0c;另一种资源协调者&#xff09;是一种新的 Hadoop 资源管理器&#xff0c;它是一个通用资源管理系统和调度平台&#xff0c;可为上层应用提供统一的资源管理和调度。支持多个数据处理框架&…

【每周一测】Java阶段三阶段考试

目录 1、SpringBoot在整合RabbitMQ时需要导入的包是 2、下列关于RabbitMQ的confirm消息确认机制解释说明正确的是 3、关于SpringBoot的配置文件&#xff0c;以下说法正确的是&#xff08;&#xff09; 4、变量命名规范说法正确的是? 5、哪个关键字可以对对象加互斥锁&…

Adversarial Attacks on Neural Networks for Graph Data

Adversarial Attacks on Neural Networks for Graph Data----《针对图数据的神经网络的对抗攻击》 论文提出了两个问题&#xff1a; 1、属性图的深度学习模型容易受攻击吗&#xff1f; 2、他们的结果可靠吗&#xff1f; 回答这两个问题需要考虑到GNN的特性&#xff1a; ①关…

Spring IOC - 推断构造方法

一、前言 上文解析了Bean生命周期的实例化阶段&#xff0c;其中bean真正开始实例化的核心代码位于方法AbstractAutowireCapableBeanFactory#createBeanInstance中&#xff0c;这里也是spring推断构造方法的核心所在。 二、整体介绍 首先看下方法的源码及注释如下&#xff0c;下…

一文带你了解QT Model/View框架的设计思想和实现机制

目录 1、QT Model/View框架简介 1.1、QT Model/View是什么&#xff1f; 1.2、QT Model/View框架核心思想 1.3、Model/View框架工作机制 1.4、Model/View框架的类 2、Model 2.1模型简介 2.2、模型索引 2.3、数据角色 2.4、QStringListModel 2.5、QFileSystemModel 2…

Pycharm中添加Python库指南

一、介绍 Pycharm是一款为Python开发者提供的集成开发环境&#xff08;IDE&#xff09;&#xff0c;支持执行、调试Python代码&#xff0c;并提供了许多有用的工具和功能&#xff0c;其中之一就是在Pycharm中添加Python库。 添加Python库有许多好处&#xff0c;比如能够增加开…

xlua源码分析(三)C#访问lua的映射

xlua源码分析&#xff08;三&#xff09;C#访问lua的映射 上一节我们主要分析了lua call C#的无wrap实现。同时我们在第一节里提到过&#xff0c;C#使用LuaTable类持有lua层的table&#xff0c;以及使用Action委托持有lua层的function。而在xlua的官方文档中&#xff0c;推荐使…

算法通关村第十关-青铜挑战快速排序

大家好我是苏麟,今天带来快速排序 . 快速排序 单边快速排序(lomuto 洛穆托分区方案) 单边循环 (lomuto分区) 要点 : 选择最右侧元素作为基准点j 找比基准点小的&#xff0c;i 找比基准点大的&#xff0c;一旦找到&#xff0c;二者进行交换。 交换时机: 找到小的&#xff0c…

第四篇 《随机点名答题系统》——基础设置详解(类抽奖系统、在线答题系统、线上答题系统、在线点名系统、线上点名系统、在线考试系统、线上考试系统)

目录 1.功能需求 2.数据库设计 3.流程设计 4.关键代码 4.1.设置题库 4.1.1数据请求示意图 4.1.2选择题库&#xff08;index.php&#xff09;数据请求代码 4.1.3取消题库&#xff08;index.php&#xff09;数据请求代码 4.1.4业务处理Service&#xff08;xztk.p…

AlphaControls控件TsDBCombobox出错:访问违规

日常使用AlphaControls控件TsDBCombobox&#xff0c;作为数据变化数据的控件。通常正常使用&#xff0c;一日 发现&#xff0c;出现以下错误&#xff1a; 控件访问违规的源代码&#xff0c;出错代码&#xff1a; function TacMainWnd.CallPrevWndProc(const Handle: hwnd; co…

基于Zemax的高能激光发射系统的扩束系统设计

关键词&#xff1a;高功率激光发射系统&#xff1b;扩束系统 1 引言 高功率激光发射系统是强激光空间传输系统中不可缺少的装置。对高功率激光发射系统的研究一直是激光应用领域的关键技术问题。高功率激光发射系统通常由准直系统、导光光路系统和扩束系统组成,光学系统要求具…

股票价格预测 | Python实现基于CNN卷积神经网络的股票预测模型(keras,Conv1D)

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 股票价格预测 | Python实现基于CNN卷积神经网络的股票预测模型(keras) 源码设计 import quandl import datetimedf = quandl