对于子数组问题的动态规划

news2024/12/24 8:28:46

前言

先讲讲我对于这个问题的理解吧

当谈到解决子数组问题时,动态规划(DP)是一个强大的工具,它在处理各种算法挑战时发挥着重要作用。动态规划是一种思想,它通过将问题分解成更小的子问题并以一种递归的方式解决它们,然后利用这些解决方案来构建原始问题的解。在动态规划中,我们经常会遇到两种状态:一种是单独成一段,另一种是以 i 结尾的子数组。

通过枚举和动态规划,我们可以有效地解决子数组问题。枚举法需要考虑所有可能的子数组组合,然后比较它们以找到最优解。这种方法通常需要较多的时间和空间,因为它需要枚举所有可能性。而动态规划则更加智能化,它通过保存历史记录来避免不必要的重复计算。这样,下次遍历时,我们可以利用之前的计算结果,从而大大提高了效率。

动态规划的一个常见技巧是前缀和,它可以帮助我们快速求出数组中任意子数组的和。前缀和的核心思想是将原始数组中每个位置的值累加起来,形成一个新的数组,然后利用这个新数组来快速计算子数组的和。这种方法在处理求子数组和的问题时非常实用,因为它将复杂度降低到了单一状态的动态规划。

另外,预处理也是动态规划中常用的技巧之一。通过将经常使用的数据存储起来,我们可以在需要时快速获取,从而减少计算时间。预处理的思想是在问题出现之前就对数据进行处理,以便在需要时能够迅速获取所需的信息。

综上所述,动态规划是一种强大的解决子数组问题的方法,通过合理利用枚举、动态规划、前缀和和预处理等技巧,我们可以高效地解决各种复杂的算法挑战,为问题提供简单明了的解决方案。

这是我记得笔记 

 

我准备了五道例题都是这些解决方案

1.求最大子数组的和

. - 力扣(LeetCode)

思路分析:这一题主要是使用动态规划,也可以使用前缀和,动态规划也是求子数组的普遍思路,有两种状态,1自己组成子数组 和 前面的组成子数组,所以状态转移方程也就是Max(nums[i],dp[i - 1] + nums[i])

 代码实现

  public int maxSubArray(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n + 1];//以i位置为结尾的最大子数组和 (多状态 前面i - 1的子数组要  和 不要)
        // 初始化 (因为存在负数)
        dp[0] = -0x3f3f3f3f;

        //前面子数组都是以i - 1位置为结尾 或者 i位置自己构成一个数组
        for (int i = 1; i <= nums.length; i++) {
            dp[i] = Math.max(nums[i - 1], dp[i - 1] + nums[i - 1]);
        }
        int max = -0x3f3f3f3f;
        for (int i = 0; i < dp.length; i++) {
            max = Math.max(dp[i], max);
        }
        return max;
    }

2.求最大环形子数组

. - 力扣(LeetCode)

思路分析:

中间的是连续的所以求内部最小子数组和就好了, 或者中间成最大子数组和
//f[]表示以i位置为结尾的所有子数组中的最大值  //g[]表示以i位置为结尾的所有子数组中的最小值
//g[]就是为了处理边界。他通过计算中间部分的最小值来结算环的最大值

public int maxSubarraySumCircular(int[] nums) {
        int sum = 0;//用来处理最小值
        int n = nums.length;
        //1.状态表示
        int[] f = new int[n + 1],g = new int[n + 1];
        //2.状态转移方程    自己组成子数组  和  自己加上以 i-1位置结尾 的最大子数组
        //3.初始化 = 0 即可
        for (int i = 1; i <= n; i++) {
            f[i] = Math.max(f[i - 1] + nums[i - 1], nums[i - 1]);
            g[i] = Math.min(g[i - 1] + nums[i - 1], nums[i - 1]);
            sum += nums[i - 1];
        }
        int maxF = -0x3f3f3f3f;//统计结果
        int minG = 0x3f3f3f3f;//统计结果
        //可以和上面统一合并,一个循环就够了
        for (int i = 1; i <= n; i++) {
            maxF = Math.max(maxF,f[i]);
            minG = Math.min(minG,g[i]);
        }
        //为了防止全是负数返回0,所以sum - minG要和0做判断
        //因为  -8 - (-8) = 0;全是负数sum = -8 minG = -8 所以要返回maxF
        return Math.max(maxF,sum - minG == 0 ? maxF : sum - minG);
    }

3.和为k的子数组个数 

. - 力扣(LeetCode)

 思路分析:

//解法 动态规划  +  hash表   k == pre[i](i位置的前缀和) - pre[j - 1] //此时 [j,i]的子数组为k
 public int subarraySum(int[] nums, int k) {
        int count = 0;//统计出现了多少次
        int n = nums.length;
        HashMap < Integer, Integer > hash = new HashMap<>();//当词典使用,存储所有前缀和
        hash.put(0,1);//记录0出现了1次,防止前缀和单独构成答案
        //1.状态表示   以i位置为结尾的区间和
        int[] pre = new int[n + 1];
        //2.状态转移方程  pre[i] = pre[i - 1] + nums[i]
        //3.初始化  防止j - 1 越界 pre[0] = 0
        for (int i = 1; i <= n; i++) {
            pre[i] = pre[i - 1] + nums[i - 1];//下标映射,因为我的pre[0]是虚拟节点
            if (hash.containsKey(pre[i] - k)){
                count += hash.get(pre[i] - k);
            }
            hash.put(pre[i],hash.getOrDefault(pre[i],0) + 1);//键为前缀和的值 ,值为出现的次数
        }
        return count;
    }

 滚动数组优化形成前缀和

//因为上述我们只使用了,pre[i - 1] 和 pre[i] 这两种状态,所以可以使用滚动数组进行优化,设置两个变量即可
    //也就是我们熟知的前缀和
    public int subarraySum1(int[] nums, int k) {
        int count = 0;//统计出现了多少次
        int n = nums.length;
        HashMap < Integer, Integer > hash = new HashMap<>();//当词典使用,存储所有前缀和
        int pre = 0;
        hash.put(0,1);//记录0出现了1次,防止前缀和单独构成答案
        for (int i = 0; i < n; i++) {
            pre += nums[i];
            if (hash.containsKey(pre - k)){
                count += hash.get(pre - k);
            }
            hash.put(pre,hash.getOrDefault(pre,0) + 1);//键为前缀和的值 ,值为出现的次数
        }
        return count;
    }

4.乘积为k的最大子数组

 

. - 力扣(LeetCode)

思路分析:注释都有明确标注状态表示和转移方程

 public int maxProduct(int[] nums) {
        int n = nums.length;
        //1.定义状态表示
        int[] f = new int[n + 1];//以i位置为结尾  所有子数组中 乘积的最大值   遇到正数我要你
        int[] g = new int[n + 1];//以i位置为结尾  所有子数组中 乘积的最小值   遇到负数我要你
        //2.状态转移方程  遇到正数我要最大值(f[i - 1])    遇到负数我要最小值(g[i - 1])
        //3.初始化  防止i - 1越界但不可保存0,因为初始化的初衷就是保证后续的位置不受影响
        f[0] = g[0] = 1;//注意多次赋值是从右往左进行的
        int ret = -0x3f3f3f3f;
        for (int i = 1; i <= n; i++) {
            if (nums[i - 1] > 0){
                f[i] = Math.max(f[i - 1] * nums[i - 1],nums[i - 1]);
                g[i] = Math.min(g[i - 1] * nums[i - 1],nums[i - 1]);
            }else {
                f[i] = Math.max(g[i - 1] * nums[i - 1],nums[i - 1]);
                g[i] = Math.min(f[i - 1] * nums[i - 1],nums[i - 1]);
            }
            ret = Math.max(ret,f[i]);
        }
        return ret;
    }

5.乘积为正数的最长子数组长度

. - 力扣(LeetCode)

 public int getMaxLen(int[] nums) {
        int n = nums.length;
        int ret = 0;//统计
        //1.状态表示
        int[] f = new int[n + 1];//以i位置为结尾中的 所有子数组中的 乘积为正数的最大长度
        int[] g = new int[n + 1];//以i位置为结尾中的 所有子数组中的 乘积为负数的最大长度
        //2.状态转移方程  f[i]如果i位置为正数为 f[i - 1] + 1    负数 g[i - 1] + 1
        //              g[i]同理正数g[i - 1] + 1   负数 f[i - 1] + 1
        //3.初始化 默认长度为0不影响后续结果
        for (int i = 1; i <= n; i++) {
            if (nums[i - 1] > 0){
                f[i] = f[i - 1] + 1;
                //当最后一个元素为正数的时候,并且g[i - 1] = 0表示前面没有负数,所以不可能组成负数
                g[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1;
            }else if (nums[i - 1] < 0){
                //当最后一个元素为负数的时候,并且g[i - 1] = 0表示前面没有负数,所以不可能组成正数
                f[i] = g[i - 1] == 0 ? 0 : g[i - 1]  + 1;
                g[i] = f[i - 1] + 1;
            }else {//处理为0的情况
                f[i] = 0;
                g[i] = 0;
            }
            ret = Math.max(ret,f[i]);
        }
        return ret;
    }

总结

当解决子数组问题时,动态规划是一个强大而智能的工具。通过将问题分解成更小的子问题并以递归的方式解决它们,动态规划可以高效地找到原始问题的解。在动态规划中,我们常常会遇到两种状态:一种是单独成一段,另一种是以 i 结尾的子数组。

枚举和动态规划是解决子数组问题的两种主要方法。枚举法需要考虑所有可能的子数组组合,然后比较它们以找到最优解。而动态规划则通过保存历史记录来避免不必要的重复计算,从而提高效率。

在动态规划中,常用的技巧包括前缀和和预处理。前缀和可以帮助我们快速求出数组中任意子数组的和,而预处理则可以在问题出现之前就对数据进行处理,以提高计算效率。

综上所述,动态规划是解决子数组问题的一种强大工具,通过合理利用枚举、动态规划、前缀和和预处理等技巧,我们可以高效地解决各种复杂的算法挑战,为问题提供简单明了的解决方案。

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

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

相关文章

Linux(openEuler、CentOS8)基于chrony企业内网NTP服务器搭建实验

一、知识点 chrony 是由 守护进程 chronyd 以及 命令行工具 chronyc 组成的 chronyd 在后台静默运行并通过 123 端口与时间服务器定时同步时间&#xff0c;默认的配置文件是 /etc/chrony.conf chronyc 通过 323 端口与 chronyd 交互&#xff0c;可监控 chronyd 的性能并在运…

EPAI手绘建模APP资源管理和模型编辑器2

g) 矩形 图 26模型编辑器-矩形 i. 修改矩形的中心位置。 ii. 修改矩形的长度和宽度。 h) 正多边形 图 27模型编辑器-内接正多边形 图 28模型编辑器-外切正多边形 i. 修改正多边形的中心位置。 ii. 修改正多边形中心距离端点的长度。 iii. 修改正多边形的阶数。阶数为3&…

LLVM Instruction Selection 笔记

Instruction Selection 所处阶段 注&#xff1a;上图来源于 Welcome to the back-end: The LLVM machine representation 可以看到 SelectionDAG 架在 LLVM IR 和 LLVM MIR 之间&#xff0c;在此之前 machine independent optimization 已经完成。之后基本上就进入了 machine …

Dynamics 365: 从0到1了解如何创建Custom API(2) - Custom API与插件关联

接上一篇文章Dynamics 365: 从0到1了解如何创建Custom API(1) - 在Power Apps中创建-CSDN博客 这次我们主要写一个插件&#xff0c;与我们之前创建的Custom API进行关联。 1. 编写插件代码 因为代码太少了&#xff0c;我就不写注释了&#xff0c;大家如果有不懂的可以在评论…

2024牛客五一集训派对day5 K.Stack

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5; c…

知乎23届数据分析校招A卷——笔记

1、and 和 or的并列运用[先看and] 条件1 OR 条件2 AND 条件3 执行顺序是先执行AND操作符&#xff08;先看条件2和3&#xff09;&#xff0c;再根据其结果判断是否需要执行OR操作符&#xff0c;并最终返回整个表达式的逻辑结果。 条件1 and 条件2 or 条件3 执行逻辑是先执行…

Swiper轮播图

版本&#xff1a;“swiper”: “^6.8.4”, 处理每分钟重新请求数据后&#xff0c;播放卡顿&#xff0c;快速闪&#xff0c;没按照设置时间播放等bug 以下是直接vue2 完整的组件代码 使用&#xff1a; <SwiperV :imgList“swiperList” / <template><div class"…

【论文阅读笔记】关于“二进制函数相似性检测”的调研(Security 22)

个人博客链接 注&#xff1a;部分内容参考自GPT生成的内容 [Security 22] 关于”二进制函数相似性检测“的调研&#xff08;个人阅读笔记&#xff09; 论文&#xff1a;《How Machine Learning Is Solving the Binary Function Similarity Problem》&#xff08;Usenix Securi…

docker-compose启动mysql5.7报错

描述一下问题经过&#xff1a; 使用docker compose 部署mysql5.7 文件如下: 使用命名卷的情况下&#xff0c;匿名卷不存在该问题 services:mysql:restart: alwaysimage: mysql:5.7container_name: mysql-devports:- 3306:3306environment:- MYSQL_DATABASEdev- MYSQL_ROOT_PAS…

备忘: 使用langchain结合千问大模型,用本地知识库辅助AI生成代码

本文主要是寻求解决把预先准备的文字需求转换为代码的方法 主要思路&#xff1a; 把某一类文字需求储存到本地知识库&#xff0c;使得用户输入需求目标&#xff0c;然后程序自动从知识库抽取相关需求&#xff0c;然后转发给在线的大模型形成代码。 工具&#xff1a; 本地在库用…

【UnityRPG游戏制作】Unity_RPG项目_玩法相关

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…

jvm 马士兵 01 JVM简介,class文件结构

01.JVM是什么 JVM是一个跨平台的标准 JVM只识别class文件&#xff0c;符合JVM规范的class文件都可以被识别 u1 是一个字节 u2是两个字节

正则表达式_字符匹配/可选字符集

正则表达式&#xff08;Regular Expression&#xff09;也叫匹配模式(Pattern)&#xff0c;用来检验字符串是否满足特 定规则&#xff0c;或从字符串中捕获满足特定规则的子串。 字符匹配 最简单的正则表达式由“普通字符”和“通配符”组成。比如“Room\d\d\d”就这样 的正则…

1.pytorch加载收数据(B站小土堆)

数据的加载主要有两个函数&#xff1a; 1.dataset整体收集数据&#xff1a;提供一种方法去获取数据及其label&#xff0c;告诉我们一共有多少数据&#xff08;就是自开始把要的数据和标签都收进来&#xff09; 2.dataloader&#xff0c;后面传入模型时候&#xff0c;每次录入数…

C++类定义时成员变量初始化

在C11中允许在类定义时对成员变量初始化。 class A { public:A() { }void show(){cout << "m_a " << m_a << endl;cout << "m_b " << m_b << endl;} private:int m_a 10;//类定义时初始化int m_b; //没有初始化…

2024阿里云ctf-web-chain17学习

agent jdk17依赖有h2思路清晰打jdbc attack <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.aliba…

踏春正当时!VELO Prevail Ride带你探索多元骑行潮流体验~

嘿&#xff0c;朋友&#xff01;踏春正当时嘞&#xff01;在这个追求个性化与多元化的新时代&#xff0c;骑行爱好者们也开始寻找能适应各种骑行场景的理想坐垫。从悠闲自在的日常通勤&#xff0c;到热血沸腾的公路竞速&#xff0c;再到勇攀高峰的山地探险&#xff0c;维乐VELO…

更深层次理解传输层两协议【UDP | TCP】【UDP 缓冲区 | TCP 8种策略 | 三次握手四次挥手】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 再谈端口号 端口号的返回…

C语言-整体内容简单的认识

目录 一、数据类型的介绍二、数据的变量和常量三、变量的作用域和生命周期四、字符串五、转义字符六、操作符六、常见的关键字6.1 关键字static 七、内存分配八、结构体九、指针 一、数据类型的介绍 sizeof是一个操作符&#xff0c;是计算机类型/变量所占内存空间的大小   sc…

AEC Capital Limited:开启可持续金融新纪元

在当今社会&#xff0c;环保和可持续发展已成为全球关注的焦点。在这个背景下&#xff0c;AEC Capital Limited作为香港的一家金融服务公司&#xff0c;以其专业、高端的服务和创新的理念&#xff0c;成为可持续金融领域的引领者。我们致力于将环境保护与金融服务相结合&#x…