【LeetCode】动态规划 刷题训练(七)

news2024/11/18 8:15:25

文章目录

  • 918. 环形子数组的最大和
    • 题目解析
    • 状态转移方程
      • f[i]状态转移方程
      • g[i]状态转移方程
    • 初始化
    • 返回值
    • 完整代码
  • 152. 乘积最大子数组
    • 题目解析
    • 状态转移方程
      • f[i]状态转移方程
      • g[i]状态转移方程
    • 初始化
    • 完整代码
  • 1567. 乘积为正数的最长子数组长度
    • 题目解析
    • 状态转移方程
      • f[i]状态转移方程
      • g[i]状态转移方程
    • 初始化
    • 完整代码

918. 环形子数组的最大和

点击查看: 环形子数组的最大和


给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。
环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。
子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], …, nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。

示例 1:
输入:nums = [1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
示例 2:
输入:nums = [5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10

题目解析

可以只取3,或者取3和-2
由于数组是环形的,所以在3和-2的基础上再取1 和-2
通过比较,取3是最大和


状态转移方程

先将环形数组转化为 普通的数组 ,然后再解决


情况1:

当前数组想要取最大和,则只需取3 即可
则当前数组的最大和就在数组内部,该数组在当前情况下可看作是普通数组
可以使用 最大子数组和的解法来解决当前问题


情况2:

当前数组想要取最大和,则需取后面的5以及 环形连接的前面的5
整段数组的和为定值,若想取 当前红色区域的最大值,则需取空白区域的最小值
由于红色区域是不连续的,而空白区域为连续区间
所以可以先求 空白区域的最小子数组和
再通过整体数组和减去 空白区域的最小数组和 则为 红色区域的最大子数组和


情况1的最大子数组和 用 f 表示
情况2的最小子数组和用 g 表示

f[i]:表示以i为结尾的所有子数组中的最大和
g[i]:表示以i为结尾的所有子数组中的最小和

f[i]状态转移方程

将子数组划分为两类

1. i位置元素本身(长度为1)
该情况下:f[i]=nums[i]

2. i位置元素加上前面元素结合(长度大于1)

因为要求的是以i位置为结尾的子数组最大和,所以应该先求 以i-1位置为结尾的子数组的最大和 即 f[i-1]
在加上nums[i] ,就为以i位置为结尾的子数组最大和
该情况下:f[i]=f[i-1]+nums[i]


状态转移方程为:
f[i]=max(nums[i],f[i-1]+nums[i])

g[i]状态转移方程

g[i]与f[i]不同的是,f[i]要的是最大值,而g[i]要的是最小值,其他分析都是相同的


g[i]可以分为两类

情况1:i位置元素本身(长度为1)
该情况下:g[i]=nums[i]

情况2:i位置元素与前面元素结合(长度大于1)

想求以i为结尾的最小子数组和,就需要先求 以i-1为结尾的最小子数组和 即g[i-1]
在加上nums[i],就为 以i为结尾的最小子数组和
该情况下:g[i]=g[i-1]+nums[i]


状态转移方程为:
g[i]=min(nums[i],g[i-1]+nums[i])

初始化

根据状态转移方程,若i为0,则会发生越界问题

为了防止这种越界问题出现,所以加入一个虚拟节点
扩展后的数组,虚拟节点处下标为0,则 原数组的元素下标从1开始


对于f[i]状态转移方程来说,若i为1,则f[1]=max(nums[1],f[0]+nums[1])
由于下标为0处为虚拟节点,所以应使f[0]不干扰结果 ,所以将f[0]置为0


对于g[i]状态转移方程来说,若i为1,则g[1]=max(nums[1],g[0]+nums[1])
由于下标为0处为虚拟节点,所以应使g[0]不干扰结果 ,所以将g[0]置为0

返回值

对于刚开始分析的两种情况
情况1
取f表中的最大值 即 fmax
fmax即 情况1的最大子数组和


情况2
取g表中的最小值即 gmin
由于情况2的红色区域的最大子数组和 为 数组整体减去 白色区域子数组和
所以 情况2的最大子数组和 为 sum-gmin

环形数组的最大子数组和 为: max(fmax,sum-gmin)


g为一个连续的子数组,和为最小,所以gmin为当前数组的三个元素全部加上才为最小和
使用sum-gmin,则会导致 情况2的最大子数组和为0
使最终求环形数组的最大子数组和 时,预期为最大负数,结果为0,造成错误


所以要加上判断条件
若 sum==gmin (数组内元素全为负),直接返回fmax 即可
若上述判断不成立,则 返回 max(fmax,sum-gmin)

完整代码

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
       int n=nums.size();
       //为了防止越界问题,所以多开辟一个空间
       vector<int>f(n+1,0);
       vector<int>g(n+1,0);
       //初始化
       f[0]=0;
       g[0]=0;
       int i=0;

       //sum为整体数组和
       int sum=0;
       for(i=0;i<n;i++)
       {
         sum+=nums[i];
       }
         //取f表中的最大值
       int fmax=INT_MIN;
         //取g表中的最小值
       int gmin=INT_MAX;
       for(i=1;i<=n;i++)
       {
           //由于当前下标为扩展数组f/g的下标
           //想要使用当前下标 去寻找 对应nums的值
           //需要下标减1
           f[i]=max(nums[i-1],f[i-1]+nums[i-1]);
           g[i]=min(nums[i-1],g[i-1]+nums[i-1]);
           fmax=max(fmax,f[i]);
           gmin=min(gmin,g[i]);
       }
        //有可能存在数组元素全为负的情况,所以需要判断
        return sum==gmin?fmax:max(fmax,sum-gmin);
    }
};

152. 乘积最大子数组

点击查看:乘积最大子数组


给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。

示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

题目解析

取2和3乘积为 子数组最大乘积
乘积变为6

若在此基础上再取 -2 ,乘一个负数就会导致 越乘越小
乘积变为 -12

状态转移方程

f[i]:表示 以i位置为结尾的 所有子数组的 最大乘积
g[i]:表示以i位置为结尾的 所有子数组的 最小乘积

f[i]状态转移方程

f[i]可以分为两类:

情况1:i位置处元素本身(长度为1)
该情况下:f[i]=nums[i]

情况2:i位置处元素与前面元素结合(长度大于1)

情况1 若nums[i]大于0
想求i位置处的最大乘积,就要先求i-1位置处的最大乘积即f[i-1]
再乘以nums[i],即 nums[i]大于0情况下,i位置处的最大乘积
f[i]=f[i-1]*nums[i]

情况2 若nums[i]小于0
若nums[i]为负,而f[i-1]为以i-1为结尾的最大乘积,就会造成越乘越小
所以需要在前面应乘以一个最小乘积 这样就会使两者越乘越大
即 以i-1为结尾的所有子数组的最小乘积 g[i-1]
再乘以nums[i] 即为 nums[i]小于0情况下,i位置处的最大乘积
f[i]=g[i-1]*nums[i]

状态转移方程为:
f[i]=max(nums[i] , max(f[i-1]*nums[i],g[i-1]*nums[i]) )

g[i]状态转移方程

情况1 i位置处元素(长度为1)
g[i]=nums[i]


情况2 i位置处元素加上前面元素结合(长度大于1)

若nums[i]大于0
想求i位置处的最小乘积,就要先求i-1位置处的最小乘积即g[i-1]
再乘以nums[i],即 nums[i]大于0情况下,i位置处的最小乘积
g[i]=g[i-1]*nums[i]


若nums[i]小于0
想求i位置处的最小乘积,就要先求i-1位置处的最大乘积即f[i-1]
再乘以nums[i],即 nums[i]小于0情况下,i位置处的最小乘积
g[i]=f[i-1]*nums[i]


状态转移方程为:
g[i]=min(nums[i] ,min(g[i-1]*nums[i],f[i-1]*nums[i]) )

初始化

若i为0时,就会发生越界问题

当i为1时
g[1]=min(nums[1] ,min(g[0]*nums[1],f[0]*nums[1]) )
为了不让f[0]或者g[0] 干扰结果,所以将 f[0]和g[0] 都置为1

完整代码

class Solution {
public:
    int maxProduct(vector<int>& nums) {
       int n=nums.size();
       //为了防止越界问题,所以进行将数组多加一个虚拟节点
       vector<int>f(n+1,0);
       vector<int>g(n+1,0);
       //初始化
       f[0]=1;
       g[0]=1;
       int i=0;
       int ret=INT_MIN;
       for(i=1;i<=n;i++)
       {
           //当前下标为扩展数组f/g的下标
           //想要使用当前下标 去寻找对应的原数组的值
           //需要将下标减1
           f[i]=max(nums[i-1],max(f[i-1]*nums[i-1],g[i-1]*nums[i-1]));
           g[i]=min(nums[i-1],min(g[i-1]*nums[i-1],f[i-1]*nums[i-1]));
           ret=max(ret,f[i]);
       }
       return ret;
    }
};

因为返回值为 最大乘积 ,所以只需返回f表中最大值即可

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

点击查看:乘积为正数的最长子数组长度


给你一个整数数组 nums ,请你求出乘积为正数的最长子数组的长度。
一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。
请你返回乘积为正数的最长子数组长度。

示例 1:
输入:nums = [1,-2,-3,4]
输出:4
解释:数组本身乘积就是正数,值为 24 。
示例 2:
输入:nums = [0,1,-2,-3,-4]
输出:3
解释:最长乘积为正数的子数组为 [1,-2,-3] ,乘积为 6 。
注意,我们不能把 0 也包括到子数组中,因为这样乘积为 0 ,不是正数。

题目解析

子数组必须为连续的,子数组的乘积为正,找到所有乘积为正的子数组中 长度 最长的哪一个

选择乘积 1 -2 -3,乘积为正数,长度为 3
选择乘积 -2 -3 4,乘积为正数,长度为 3
选择乘积 1 -2 -3 4,乘积为正数,长度为 4
所以最长 长度为4

状态转移方程

f[i]:表示以i位置为结尾的 所有子数组中 乘积为正数的 最长长度
g[i]:表示以i位置为结尾的 所有子数组中 乘积为负数的 最长长度

f[i]状态转移方程

情况1 i位置处元素本身 (长度为1)

若num[i]大于0,则f[i]为1
若nums[i]小于0,则f[i]为0


情况2 i位置元素 加上前面元素结合(长度大于1)

若nums[i[大于0,则前面需乘以乘积为正数的最长长度

想求以i位置为结尾的 所有子数组中 乘积为正数的 最长长度,因为nums[i]大于0,则需先求以i-1位置结尾的 所有子数组中 乘积为正数的 最长长度即 f[i-1]
在加上后面i位置处的长度 即+1

该情况下: f[i]=f[i-1]+1


若nums[i]小于0 ,则前面需乘以乘积为负数的最长长度

这样才可以保证整体 乘积为正数

想求以i位置为结尾的 所有子数组中 乘积为正数的 最长长度,因为nums[i]小于0,则需先求以i-1位置结尾的 所有子数组中 乘积为负数的 最长长度即 g[i-1]
在加上后面i位置处的长度 即+1

如果直接写成 f[i]=g[i-1]+1
若数组中以i-1为结尾的乘积全都大于0,i位置处小于0,则没办法生成乘积为正数的长度,预期结果为0,f[i]值为1,造成结果错误
所以需先判断 g[i-1]是否等于0(若等于0说明以i-1为结尾的乘积全都大于0)
若g[i-1]等于0,则f[i]=0
若g[i-1]不等于0,则f[i]=g[i-1]+1
即 f[i] =g[i-1]==0?0:g[i-1]+1


状态转移方程为:
if(nums[i]>0)
f[i]=f[i-1]+1

if(nums[i]<0)
f[i]=g[i-1]==0?0:g[i-1]+1

g[i]状态转移方程

情况1 长度为1

g[i]表示以i结尾的所有子数组中 乘积为负的最长长度
若nums[i]大于0,则g[i]为0
若num[i]小于0,则g[i]为1


情况2 长度大于1

若nums[i]大于0 ,则前面需乘以 乘积为负的最长长度
使整体数组 乘积 为负

想求以i位置为结尾的 所有子数组中 乘积为负数的 最长长度,因为nums[i]大于0,则需先求以i-1位置结尾的 所有子数组中 乘积为负数的 最长长度即 g[i-1]
在加上后面i位置处的长度 即+1

如果直接写成 g[i]=g[i-1]+1
若数组中以i-1为结尾的乘积全都大于0,i位置处大于0,则没办法生成乘积为负数的长度,预期结果为0,f[i]值为1,造成结果错误
所以需先判断 g[i-1]是否等于0(若等于0说明以i-1为结尾的乘积全都大于0)
若g[i-1]等于0,则g[i]=0
若g[i-1]不等于0,则g[i]=g[i-1]+1
即 g[i] =g[i-1]==0?0:g[i-1]+1


若nums[i]小于0,则前面需乘以乘积为正的最长 长度

想求以i位置为结尾的 所有子数组中 乘积为负数的 最长长度,因为nums[i]小于0,则需先求以i-1位置结尾的 所有子数组中 乘积为正数的 最长长度即 f[i-1]
在加上后面i位置处的长度 即+1

该情况下: g[i]=f[i-1]+1


状态转移方程为:
if(nums[i]>0)
g[i]=g[i-1]==0?0:g[i-1]+1

if(nums[i]<0)
g[i]=f[i-1]+1

初始化

若i为0时,就会发生越界问题

当nums[i]小于0时,若i为1,则g[1]=f[0]+1
为了不让f[0]干扰结果,所以将f[0]=0


由于当前使用下标为f/g扩展数组的下标
所以想要使用当前下标 ,寻找到对应nums的值
需要下标减1
所以当i为1时,取nums[0]值,若nums[0]>0,则g[0]==0
(g[0]表示以下标0为结尾的所有子数组的乘积负数的最长长度)
nums[0]大于0,说明没有乘积为负的情况,所以g[0]取0

完整代码

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
       int n=nums.size();
       //为了避免越界问题,所以设置一个虚拟节点
       vector<int>f(n+1,0);
       vector<int>g(n+1,0);
       //初始化
       f[0]=0;
       g[0]=0;
       int i=0;
       int ret=INT_MIN;
       for(i=1;i<=n;i++)
       {
           //由于当前使用下标为f/g扩展数组的下标
           //所以想要使用当前下标 ,寻找到对应nums的值
           //需要下标减1
            if(nums[i-1]>0)
            {
                f[i]=f[i-1]+1;
                g[i]=g[i-1]==0?0:g[i-1]+1;
            }
            else if(nums[i-1]<0)
            {
                f[i]=g[i-1]==0?0:g[i-1]+1;
                g[i]=f[i-1]+1;
            }
            ret=max(ret,f[i]);
       }
       //返回 f表中的最大值
       return ret;
    }
};

因为f表 表示乘积为正的子数组的最长长度,所以返回f表的最大值

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

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

相关文章

前端-盒子模型

元素显示模式 块级 行内 行内块 外边距折叠现象 合并现象 塌陷现象 &#xff08;1&#xff09;合并现象 场景&#xff1a;垂直布局的块级元素&#xff0c;上下的 margin 会合并 结果&#xff1a;最终两者距离为 margin 的最大值 解决方法&#xff1a;只给其中一个盒子设置 …

u盘ntfs和fat32哪个好 把u盘改成ntfs有什么影响

u盘在日常生活中的使用频率很高&#xff0c;许多用户在选购u盘时很少会注意到u盘格式&#xff0c;但u盘的格式对u盘的使用有很大影响。u盘格式有很多&#xff0c;常见的有ntfs和fa32&#xff0c;u盘ntfs和fat32哪个好&#xff1f;这要看u盘的使用场景。把u盘改成ntfs有什么影响…

简要记录java 锁

Java中的锁机制主要分为Lock和Synchronized. Synchronized在JVM里的实现是基于进入和退出Monitor对象来实现方法同步和代码块同步的。monitorenter指令是在编译后插入到同步代码块的开始位置&#xff0c;而monitorexit是插入到方法结束处和异常处&#xff0c;JVM要保证每个mon…

datatable刷新数据,js不整体刷新页面,使用DataTables表格插件定时更新后台数据变化

文章目录 前言一、meta的http-equiv属性二、使用DataTables表格插件2.1.整体思路2.2.将$(#myTableId).DataTable({……}&#xff09;封装成函数2.3刷新表格数据函数2.4统一调用刷新表格的自动加载函数2.4定时间隔执行刷新自动加载函数 前言 最近遇到一个需求&#xff0c;需要刷…

【新版系统架构】第七章-系统架构设计基础知识(架构风格、复用)

软考-系统架构设计师知识点提炼-系统架构设计师教程&#xff08;第2版&#xff09; 第一章-绪论第二章-计算机系统基础知识&#xff08;一&#xff09;第二章-计算机系统基础知识&#xff08;二&#xff09;第三章-信息系统基础知识第四章-信息安全技术基础知识第五章-软件工程…

安装两个mysql

标题:安装两个mysql 参考blog&#xff1a;MySQL–修改mysql服务可执行文件的路径&#xff08;Windows&#xff09; 参考视频&#xff1a;mysql安装-安装多个mysql方法 安装第一个&#xff0c;网上有很多教程&#xff0c;这里就附上一个链接了&#xff1a;mysql5.5安装 安装第…

JS知识点汇总(七)--数据类型

1. JavaScript中的简单数据类型有哪些&#xff1f; 1、概述 JS 中有六种简单数据类型&#xff1a;undefined、null、boolean、string、number、symbol ES10中的新特性 BigInt (任意精度整数)&#xff0c;目前还处于stage-4阶段&#xff0c;不出意外即将成为js的第七种基本数据…

036:mapboxGL点击某位置,转换坐标为地址,弹出地理信息

第036个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中点击某位置,转换坐标位地址,弹出地理信息. 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共106行)相关API参考:专栏目标示例效果 配置方式 1)…

完整的复数类

复数类应该具有的操作 运算&#xff1a;&#xff0c;-&#xff0c;*&#xff0c;/ 比较&#xff1a;&#xff0c;! 赋值&#xff1a; 求模&#xff1a;modulus 利用操作符重载 统一复数与实数的运算方式 统一复数与实数的比较方式 注意事项 C 规定赋值操作符 () 只能重载…

vue项目运行不起来,可能是版本等不兼容问题

给pakeage.json 运行脚本前加上&#xff1a;set NODE_OPTIONS--openssl-legacy-provider && 即可。

echarts 实现3D饼图

2023.6.30今天我学习了如何使用echarts渲染一个3d的饼图&#xff0c;效果如下&#xff1a; 相关代码如下&#xff1a; <template><div ref"pie3d"/> </template> <script>mounted() {this.myChart this.$echarts.init(this.$refs.pie3d);…

【AUTOSAR】BMS开发实际项目讲解(二十五)----电池管理系统安全状态过渡

安全状态过渡 关联的系统需求 TSR-BMS-S201、TSR-BMS-S202、TSR-BMS-S203、TSR-BMS-S204、TSR-BMS-S601、TSR-BMS-S602、TSR-BMS-S603、TSR-BMS-S604、TSR-BMS-S605、TSR-BMS-S606、TSR-BMS-S607、TSR-BMS-S608、TSR-BMS-S609、TSR-BMS-S610、TSR-BMS-S611、TSR-BMS-S612; TSC…

【Linux系统编程】—进程学习笔记(fork进程创建、退出、僵死进程与孤儿进程、如何避免僵死进程)

目录 一、进程关键概念 二、进程创建实战 1、fork函数 2、fork创建一个子进程的一般目的&#xff1a; 3、fork函数实例&#xff1a; 4、fork的写时拷贝技术&#xff08;COW&#xff09; 三、进程退出 1、正常退出 2、异常退出 3、总结 四、僵死进程与孤儿进程 1、什…

融合学习:跨文化交流的学习平台

在全球化的时代&#xff0c;跨文化交流已经成为了一个不可避免的现象。在这种情况下&#xff0c;融合学习平台成为了一个非常重要的工具&#xff0c;可以帮助人们更好地了解和学习不同文化之间的差异和相似之处。本文将探讨融合学习平台的重要性&#xff0c;以及如何选择最佳的…

构建交互式数据框架:使用Gradio的Dataframe模块

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

查找算法-线性搜索

线性搜索 简介 线性搜索是一种简单的搜索算法&#xff0c;也被称为顺序搜索。它从数据集的起始位置开始&#xff0c;逐个比较每个元素&#xff0c;直到找到目标元素或遍历完整个数据集为止。如果目标元素存在于数据集中&#xff0c;线性搜索会返回该元素的位置或索引&#xf…

照片如何转存到手机笔记中?具体方法教程在这里

传统的笔记形式一般是以文字的形式记录&#xff0c;通常是在一个笔记本中写下自己的所感所想、工作、生活等方面的内容。有时也有人会选择贴上照片&#xff0c;成为个人回忆的重要资料。 而随着手机笔记的出现&#xff0c;很多人选择使用它来记录&#xff0c;因为我们可以随时…

小米 红米 Redmi note11 4G 5G 手机解锁BL 秒BL解锁 教程 跳过168小时 selenes evergo线刷机包下载

红米&#xff08;Redmi&#xff09;Note 11 5G 手机BL解锁 红米note11 4G/5G 秒解锁BL锁 方法 教程 跳过168小时 新版本 selenes evergo 红米Note11系列版本非常多&#xff0c;从4G开始一直到后面出现的11R&#xff0c;我们常见的机型有红米 Note11/11Pro/11Pro/11SE/11R等等&…

管理类联考——数学——知识篇——公式——最难记

立方和与立方差公式 a 3 b 3 ( a b ) ( a 2 ∓ a b b 2 ) a^3b^3(ab)(a^2∓abb^2) a3b3(ab)(a2∓abb2) 一元二次方程求根公式 x − b b 2 − 4 a c 2 a &#xff0c; b 2 − 4 a c ≥ 0 x\frac{-b\sqrt{b^2-4ac}}{2a}&#xff0c;\sqrt{b^2-4ac}≥0 x2a−bb2−4ac ​​&…

蓝牙模块(HC-05/HC-06)详解

这里写目录标题 0. 蓝牙概述蓝牙技术的特点 1. 常见的蓝牙模块2. HC-05/HC-062.1 概念2.2 区别 3. STM32使用HC-05通信3.1 方法3.2 示例代码 0. 蓝牙概述 蓝牙&#xff08;Bluetooth&#xff09;是一种用于无线通信的技术标准&#xff0c;允许设备在短距离内进行数据交换和通信…