最大子矩阵:前缀和、动态规划

最近在学习动态规划,在牛客上刷题时碰到了这一题。其实最初的想法是暴力和前缀和,但是时间复杂度极高,需要套4层循环。后来去网上搜了一下相关的题解和做法,进而了解到了前缀和+线性动态规划的做法。但是在成功做出这题之前,个人感觉所搜到的博主讲解偏向于代码的编写,对于我这种初入算法的小白来说还是蛮费力气的,所以本节内容我将和大家一起从算法原理到代码一一剖析,争取写出清晰且容易理解的算法思路。

题目链接:最大子矩阵_牛客题霸_牛客网 (nowcoder.com)

 

首先我们来明确一下题目要求:本题要求的是我们在一个给定的N x N 的“矩阵”中找到 “元素和”最大的“子矩阵”。这个N x N 的矩阵其实就是一个二维数组,我们把它理解为一个矩阵。那么它的子矩阵就是矩阵中的某一片矩阵型的区域,那所要求的“最大子矩阵”的自然是找到在所有子矩阵中,矩阵元素和最大的那个矩阵。

我们先来看示例1来帮助我们理解一下题意:

我们可以发现,除该子矩阵外,在其他任意地方随意圈出一个子矩阵中的元素和均比图示矩阵的元素和小,此时我们就找到了该矩阵的最大子矩阵。

以下是两种解法:

 

 

说实话,第一种纯暴力解法就不用看了,数据量稍微大一点,就超时了。

第二种使用二维数组前缀和对原始数组进行了预处理,得到了二维前缀和数组,之后在我们求圈定范围的子矩阵元素和时会带来不小便利,但是4层循环O(n^4)的时间复杂度仍然不可小觑。 

那么我们有什么方法能减少时间复杂度呢?

我们先来了解一维数组的前缀和:

一维数组的前缀和是指将数组中从起始位置到当前位置的所有元素相加的结果,即上图中的prefix数组。

我们再来复习一下求一维数组最大子序列所使用到的线性dp算法:

算法原理:

状态表示:dp[i]所表示的是以i结尾的所有子数组中元素的最大和。 

状态转移方程:用来更新动态规划数组dp[i]:

  • nums[i - 1]表示当前位置i对应的原始数组 nums 中的元素值。

  • dp[i - 1] + nums[i - 1] 表示从当前位置向前延伸的子序列的和,即以 nums[i - 1] 结尾的子序列和加上当前位置的元素值。这个值表示了当前位置开始的新子序列的和。

  • max(nums[i - 1], dp[i - 1] + nums[i - 1]) 选择了两种情况中的较大值:第一种情况是只包含当前元素 nums[i - 1],即以当前位置 i 结尾的子序列。第二种情况是将当前位置的元素加入到之前的子序列中,即从当前位置开始新的子序列。

  • 将较大值更新到 dp[i],表示以 nums[i - 1] 结尾的最大子序列和。

这样,通过动态规划数组 dp 的更新,每个位置 i 都计算出了以该位置结尾的最大子序列和,最终找到整个数组的最大子序列和。

由于最后要输出的是dp数组中的最大值,我们可以使用一个变量来记录以 i 位置为结尾的最大子序列和,这样空间复杂度就从O(N)减少到了O(1)。

 其实,对于我们这一题,也可以使用一维数组前缀和与线性dp来解决,我们只需要将二维数组“压缩”为一维数组就好了。那么压缩方式自然是使用前缀和了。

怎么压缩呢?压缩后如何使用前缀和搭配线性dp解题呢?我们接着往下看:

进行完这一步操作后,第 i 行中第 j 列的元素即为:第 j 列从起始位置到当前位置 i 的所有元素相加的结果。 

其实通过上面的例子,我们不难发现进行预处理后的前缀和数组,仍然可以表示原数组的元素,更方便的是对于求原数组圈定矩阵元素和,在处理后的数组中仅通过使用末行数组元素减去起始行前一行的数组元素就可以得到所求矩阵的元素和。

通过不同的末行对起始行的减法操作,我们最后可以得到如下序列:

 通过前后两张图的解析,其实我们不难发现在最终生成的任意一个子序列中,随机取一段连续的数字即可表示原数组中的子矩阵。即原矩阵中所有的子矩阵均可由生成的子序列得到。

大家一定要好好理解并验证上面的两张图和两段话,当理解通透了才便于进行后续代码的编写。

既然 原矩阵中所有的子矩阵均可由生成的子序列得到,那么最大子矩阵必定是所有子序列数组中的最大连续子序列(注意:是上面通过两层循环得到的10个子序列数组中最大连续子序列元素和)。

那么我们接下来的目标很简单,就是对10个子序列数组中的每一个进行对最大连续子序列元素和的求解。所以我们需要再加上一层循环,用来遍历子序列数组。并且我们需要一个变量 ans 来记录每个子序列数组中的最大子序列元素和,需要注意的是 ans 在每次进入循环之前要更新为0,防止对后续最大子序列的求解造成影响。同时需要一个变量maxi 来记录所有子序列数组中最大的连续子序列元素和,而这个值就是我们的最大子矩阵元素和。 

最终代码:

通常我们会对二维数组多申请一行和一列的空间并初始化为0,是为了dp的状态转移方程在使用时不需要对边界情况进行特殊处理,并且不对dp数组元素的结构造成影响,提高代码的简洁性。

#include <iostream>
#include <vector>
using namespace std;

const int N = 101; // 数组的最大大小

int main() {
    int n = 0;
    cin >> n; 
    
    // 初始化大小为 (n+1) x (n+1) 的二维数组,所有元素为 0
    vector<vector<int> > arr(n + 1, vector<int>(n + 1, 0)); 

    // 初始化大小为 (n+1) x (n+1) 的二维向量 dp,所有元素为 0
    vector<vector<int> > dp(n + 1, vector<int>(n + 1, 0)); 

    // 输入矩阵的元素,并计算每列的前缀和
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            cin >> arr[i][j];
            dp[i][j] = dp[i - 1][j] + arr[i][j]; // 计算每列的前缀和
        }
    }

    int maxi = -128 * 1E4; // 初始化最大值为一个该题中最小的数
    int ans = 0;

    // 遍历所有的行
    for(int i = 0; i < n; i++)  // 从第 0 行到第 n-1 行
    {
        for(int j = i + 1; j <= n; j++) // 从第 1 行到第 n 行
        { 
            ans = 0;
            // 遍历每一列,并计算当前行对的最大子序列和
            for(int k = 1; k <= n; k++)  // 遍历每一列的元素
            {
                // 计算当前行对的最大子序列和,并更新 ans
                ans = max(dp[j][k] - dp[i][k], ans + dp[j][k] - dp[i][k]);

                // 更新目前为止找到的最大子序列和(maxi)
                maxi = max(ans, maxi);
            }
        }
    }

    cout << maxi << endl; // 输出最大子序列和
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【爬虫基础1.1课】——requests模块上

目录索引 requests模块的作用&#xff1a;实例引入&#xff1a; 特殊情况&#xff1a;锦囊1&#xff1a;锦囊2: 这一个栏目&#xff0c;我会给出我从零开始学习爬虫的全过程。感兴趣的小伙伴可以关注一波&#xff0c;用于复习和新学都是不错的选择。 那么废话不多说&#xff0c…

接搭建仿美团、代付系统源码搭建教程

最近很多粉丝催更、分享一下地球号&#xff1a;xiaobao0214520(WX) 现在大家都很流行搞网恋&#xff0c;我们搭建一个跟美团相似的系统 然后开发一个好友代付&#xff0c;我们在点单的时候转发链接让网恋对象付钱 若只是单点外卖的话&#xff0c;能榨出的油水还是太少。 所以…

Redis的数据淘汰策略——Java全栈知识(19)

Redis的数据淘汰策略 什么是数据淘汰策略 数据过期策略是 redis 中设置了 TTL 的数据过期的时候 Redis 的处理策略。数据淘汰策略是 Redis 内存不够的时候&#xff0c; 数据的淘汰策略&#xff1a;当 Redis 中的内存不够用时&#xff0c;此时在向 Redis 中添加新的 key, 那么…

免费思维13招之八:跨行业思维

免费思维13招之八:跨行业思维 免费思维的另一大战略思维——跨行业型思维。 跨行业型思维有两种:一种是通过跨行业,把自己的产品免费掉,从而赚取其他行业的利润。另一种是通过跨行业,把别人的主流产品免费掉,从而增大自己产品的销量。 第一种,把自己的产品免费,从而赚…

ONES 功能上新 | 近期产品新功能一览

支持在 ONES Project 中通过弹窗查看、编辑 ONES Wiki 页面。 应用场景&#xff1a; 当需要在 ONES Project 中查看 ONES Wiki 的页面内容时&#xff0c;可以直接点击工作项关联的 ONES Wiki 页面或项目文档组件中的页面&#xff0c;即可在 ONES Project 中通过弹窗查看 ONES W…

问题解决记录 | 内存溢出

报错截图&#xff1a; 处理方式&#xff1a; 增大PDI工具的内存 打开Spoon.bat配置文件 修改配置

【Linux 网络】网络编程套接字 -- 详解

⚪ 预备知识 1、理解源 IP 地址和目的 IP 地址 举例理解&#xff1a;&#xff08;唐僧西天取经&#xff09; 在 IP 数据包头部中 有两个 IP 地址&#xff0c; 分别叫做源 IP 地址 和目的 IP 地址。 如果我们的台式机或者笔记本没有 IP 地址就无法上网&#xff0c;而因为…

与 Apollo 共创生态:Apollo 七周年大会带我体会自动驾驶技术的发展

前言 自动驾驶技术作为当今科技领域的热门话题&#xff0c;吸引着无数开发者和企业的目光。而在这个风起云涌的行业中&#xff0c;Apollo开放平台作为自动驾驶领域的领军者之一&#xff0c;扮演着不可或缺的角色。七年前&#xff0c;当Apollo开放平台刚刚起步时&#xff0c;也…

STM32串口通信入门

文章目录 一、串口协议和RS-232标准&#xff0c;以及RS232电平与TTL电平的区别1.串口通信协议2.RS-232标准3.RS232电平与TTL电平的区别4.USB/TTL转232“模块&#xff08;CH340芯片为例&#xff09; 二、补充实验&#xff08;一&#xff09;几个常见的库函数、结构体1.时钟配置函…

java入门-面向对象的三大特性

面向对象三大特性 封装 什么是封装 封装 是将代码及其处理的数据绑定在一起的一种编程机制&#xff0c;该机制保证了程序和数据都不受外部干扰且不被误用。 封装的作用 访问控制符 方法传参-值传递 传参类型是基本类型 程序案例&#xff1a; public static void main(St…

Spring Boot 自动装配

本篇主要介绍Spring Boot 自动装配的相关内容。 目录 一、什么是自动装配 二、Bean的扫描方式 ComponentScan Import ImportSelector接口 三、Spring Boot自动装配原理 一、什么是自动装配 在我们在创建Spring Boot项目时往往会根据项目需求&#xff0c;引入很多第三方…

Spring高手之路18——从XML配置角度理解Spring AOP

文章目录 1. Spring AOP与动态代理1.1 Spring AOP和动态代理的关系1.2 AOP基本术语 2. 通过XML配置实现Spring AOP2.1 添加Spring依赖2.2 定义业务接口和实现类2.3 定义切面类2.4 配置XML 1. Spring AOP与动态代理 1.1 Spring AOP和动态代理的关系 Spring AOP使用动态代理作为…

AI 问答 API 对接说明

我们知道&#xff0c;市面上一些问答 API 的对接还是相对没那么容易的&#xff0c;比如说 OpenAI 的 Chat Completions API&#xff0c;它有一个 messages 字段&#xff0c;如果要完成连续对话&#xff0c;需要我们把所有的上下文历史全部传递&#xff0c;同时还需要处理 Token…

Matlab/simulink永磁直驱风机的建模仿真

Matlab/simulink直驱永磁同步风机的建模仿真&#xff0c;跟随风速波动效果好&#xff0c;可以作为后期科研的基础模型

【recast-navigation-js】通过websocket获取navmesh数据并初始化

目录 说在前面目录结构websocket服务器前端结果 说在前面 操作系统&#xff1a;windows 11浏览器&#xff1a;edge版本 124.0.2478.97recast-navigation-js版本&#xff1a;0.29.0golang版本&#xff1a;1.21.5 目录结构 D:. │ go.mod │ go.sum │ main.go // websocket …

电视剧电影原声背景音乐,经典影视配乐片段音效合集

一、素材描述 本套影视配乐素材&#xff0c;大小1.89G&#xff0c;27个压缩文件。 二、素材目录 宰相刘罗锅配乐片段.rar 影视配乐65首.rar 太极张三丰原声.rar 东邪西毒原声配乐15首.rar 东方不败之风云再起配乐24首.rar 东方不败原声配乐16首.rar 电影大话西游原声配…

Ubuntu18.04解决有线网卡连接问题(不更新内核成功版)

https://www.realtek.com/Download/List?cate_id584 &#xff08;需要翻一下&#xff09; 不想自己去下载&#xff0c;直接去我资源里下载我上传的包就好啦(&#x1f602;&#x1f602;&#x1f602;刚刚看了下别人下载要VIP还是自己去网站下很快的) 下载后解压&#xff0c;在…

Spring MVC(建立连接 + 请求)

文章目录 一、建立客户端和服务器的连接二、如何构造请求&#xff08;传参&#xff09;2.1 构造请求方式 参数通用注解2.2 传递单个参数2.3 传递多个参数2.4 传递数组/集合2.5 传递对象2.6 传递JSON 三、相关的其他请求操作3.1 获取URL中的参数 PathVariable3.2 上传文件 Requ…

HCIP-Datacom-ARST自选题库_07_割接【35道题】

一、单选题 1.在割接的测试阶段&#xff0c;符合以下哪一种情况的可以判断为割接成功? 网络承载的上层应用业务测试正常 网络设备的配置查看结果正常 网络流量路径正常 路由协议运行正常 2.在割接的测试阶段中&#xff0c;表明已经完成测试的标准是: IP设备的配置查看结…

Docker 直接运行一个 Alpine 镜像

由于镜像很小&#xff0c;下载时间往往很短&#xff0c;读者可以直接使用 docker run 指令直接运行一个 Alpine 容器&#xff0c;并指定运行的 Linux 指令&#xff0c;例如&#xff1a; PS C:\Users\yhu> docker run alpine echo 123 Unable to find image alpine:latest lo…