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

news2024/11/25 16:41:17

文章目录

  • 123. 买卖股票的最佳时机 III
    • 题目解析
      • 零笔交易
      • 一笔交易
      • 两笔交易
    • 状态转移方程
      • f[i][j]状态转移方程
      • g[i][j]状态转移方程
    • 初始化
    • 完整代码
  • 188. 买卖股票的最佳时机 IV
    • 题目解析
    • 状态转移方程
      • f[i][j]状态转移方程
      • g[i][j]状态转移方程
    • 初始化
    • 完整代码
  • 53. 最大子数组和
    • 状态转移方程
    • 初始化
    • 完整代码

123. 买卖股票的最佳时机 III

点击查看:买卖股票的最佳时机 III


给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

题目解析

相对于之前的股票问题,去除了手续费和冷冻期,其他大部分相同,
但是交易的次数从一次可以变为两次(最多为两次,也可以选择一次或者零次)

从买入股票到 卖出股票 ,才算完成一笔交易


零笔交易

由于价格是降序的,所以无论什么时候买股票,再卖出股票都是会亏本的
所以在这个期间内,什么都不做,此时的利润为:0
此时完成零笔交易

一笔交易

当价格为升序的时候,在第一天卖出股票,在价格为5之前什么都不做,
在价格为5时,卖出股票,此时 利润为:5-1=4
此时完成 一笔交易


两笔交易

在第一天买入股票,由于第二天要是买的话根本没有利润,所以第二天什么都不做
在第三天的时候,将股票卖出 ,此时的利润为: 5-3=2


在第四天买入股票,一直到 价格为4块之前都处于什么都不干的状态
在价格为4块时,卖出股票
此时的利润为:4-0=4
完成两笔交易的总利润为:4+2=6

此时完成两笔交易

状态转移方程

dp[i]:表示第i天结束后,所能获得的最大利润


在i位置处,共有两种状态,买入状态和卖出状态
用f表示买入状态,用g表示卖出状态
通过i表示第i天结束
通过j表示交易次数

f[i][j]:表示从第i天结束后,完成了j笔交易,处于买入状态,此时的最大利润
g[i][j]:表示从第i天结束后,完成了j笔交易,处于卖出状态,此时的最大利润

在完成 买入股票,卖出股票的操作后,会改变交易次数


f[i][j]状态转移方程

若第i-1天为买入状态,则第i天啥也不干,第i天也为买入状态
该情况下:f[i][j]=f[i-1][j];


若第i-1天为卖出状态,则第i天为买入状态
需要减去买股票对应的花费 price[i]
该情况下: f[i][j]=g[i-1][j]-price[i];


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

g[i][j]状态转移方程

若第i-1天为卖出状态,则第i天什么都不干,则第i天也为卖出状态
该情况下:g[i][j]=g[i-1][j];


若第i-1天为买入状态,则第i天为卖出状态
需要加上卖股票对应的利润 price[i] ,
因为完成了 从 买入到卖出的状态,第i天的交易次数+1 即变为 j,此时的j属于在原来的次数上+1
而第i-1天的交易次数依旧为原来的次数 ,所以应为 j-1
从买入股票到 卖出股票 ,才算完成一笔交易
假设j为0,则第i-1天买入股票,交易次数为0次
而第i天卖出股票,交易次数为1次

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


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

初始化

对于g[i][j]的状态转移方程,当j为0时,在第i-1天完成-1笔交易,这种情况是不存在的
所以可以对g[i][j]的状态转移方程进行修改


在第一步中,将g[i][j]赋值为 g[i-1][j],所以在if循环中直接将g[i-1][j]替换成g[i][j]
这样就能避免 出现-1笔交易的发生


纵坐标表示 第i天结束之后
横坐标 表示 完成 i笔交易
当 纵坐标取0时,表示第0天结束之后 ,完成 0 /1/ 2笔交易,这种情况是不存在的
当天买了又卖,是没有任何利润的,而交易次数又是有限的,
所以为了不干扰后续结果,所以将第0天结束之后 ,完成 1/ 2笔交易时都设置为 负无穷大


f[0][0] :表示第0天结束之后,处于买入状态,即将股票买了,需要花钱,利润为负
f[0][0]=-price[0];


g[0][0]:表示第0天结束之后,处于卖出状态 (第0天结束之后,没有股票在手里,无法卖出,相当于啥也没干,利润为0)
g[0][0]=0;


若负无穷大 选择 INT_MIN ,则会发生越界问题
负无穷大 选择 -0x3f3f3f3f ( 0x3f3f3f3f 为int的最大值的一半)

完整代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
       int n=prices.size();
       //因为可能完成 0 1 2 三笔交易其中一种 所以定义为3列
       //负无穷大 若要设置为INT_MIN会发生越界问题
       //所以使用 -0x3f3f3f
       vector<vector<int>>f(n,vector<int>(3,-0x3f3f3f3f));
       vector<vector<int>>g(n,vector<int>(3,-0x3f3f3f3f));
       int i=0;
       int j=0;
       //初始化
       f[0][0]=-prices[0];
       g[0][0]=0;
       for(i=1;i<n;i++)
       {
           for(j=0;j<3;j++)
           {
               f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i]);
               //修改后的状态转移方程 
               g[i][j]=g[i-1][j];
               if(j-1>=0)
               {
                   g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
               }
           }
       }
       int ret=INT_MIN;
        //寻找最后一行g的最大值
        for(j=0;j<3;j++)
        {
            if(ret<g[n-1][j])
            {
                 ret=g[n-1][j];
            }
        }
        //返回最后一行g的最大值
        return ret;
    }
};

返回值
由于 最大利润 有可能为 0笔交易/1一笔交易/2笔交易
若使用f ,则为达到最后位置,还在买入状态,不可能是最大利润
所以使用 g ,并统计 g的最后一行(0,1,2位置)的最大值

188. 买卖股票的最佳时机 IV

点击查看:买卖股票的最佳时机 IV


给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格,和一个整型 k 。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:
输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:
输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

题目解析

该题与买卖股票的最佳时机 III 基本类似,只不过把之前的 最多2次(0 1 2 三种可能) 变为了 k次( [0,k-1] 种可能


k为2,表示 最多进行2笔交易(0笔交易/1笔交易 /2笔交易 三种可能) 求最大利润


在第一天时买入股票,在第二天卖出股票,此时的利润为:4-2=2
第三天因为是最后一天,所以啥也不干
即 最大利润为:2

状态转移方程

(最佳时机IV这道题与 最佳时机III 在分析阶段 ,基本都是一致的)


dp[i]:表示第i天结束后,所能获得的最大利润

在i位置处,共有两种状态,买入状态和卖出状态
用f表示买入状态,用g表示卖出状态
通过i表示第i天结束
通过j表示交易次数

f[i][j]:表示从第i天结束后,完成了j笔交易,处于买入状态,此时的最大利润
g[i][j]:表示从第i天结束后,完成了j笔交易,处于卖出状态,此时的最大利润

在完成 买入股票,卖出股票的操作后,会改变交易次数

f[i][j]状态转移方程

若第i-1天为买入状态,则第i天啥也不干,第i天也为买入状态
该情况下:f[i][j]=f[i-1][j];


若第i-1天为卖出状态,则第i天为买入状态
需要减去买股票对应的花费 price[i]
该情况下: f[i][j]=g[i-1][j]-price[i];


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

g[i][j]状态转移方程

若第i-1天为卖出状态,则第i天什么都不干,则第i天也为卖出状态
该情况下:g[i][j]=g[i-1][j];


若第i-1天为买入状态,则第i天为卖出状态
需要加上卖股票对应的利润 price[i] ,
因为完成了 从 买入到卖出的状态,第i天的交易次数+1 即变为 j,此时的j属于在原来的次数上+1
而第i-1天的交易次数依旧为原来的次数 ,所以应为 j-1
从买入股票到 卖出股票 ,才算完成一笔交易
假设j为0,则第i-1天买入股票,交易次数为0次
而第i天卖出股票,交易次数为1次

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


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

初始化

对于g[i][j]的状态转移方程,当j为0时,在第i-1天完成-1笔交易,这种情况是不存在的
所以可以对g[i][j]的状态转移方程进行修改


在第一步中,将g[i][j]赋值为 g[i-1][j],所以在if循环中直接将g[i-1][j]替换成g[i][j]
这样就能避免 出现-1笔交易的发生


假设k为2,则有 0笔交易 1笔交易 2笔交易 三种情况

纵坐标表示 第i天结束之后
横坐标 表示 完成 i笔交易
当 纵坐标取0时,表示第0天结束之后 ,完成 0 /1/ 2笔交易,这种情况是不存在的
当天买了又卖,是没有任何利润的,而交易次数又是有限的,
所以为了不干扰后续结果,所以将第0天结束之后 ,完成 1/ 2笔交易时都设置为 负无穷大


f[0][0] :表示第0天结束之后,处于买入状态,即将股票买了,需要花钱,利润为负
f[0][0]=-price[0];


g[0][0]:表示第0天结束之后,处于卖出状态 (第0天结束之后,没有股票在手里,无法卖出,相当于啥也没干,利润为0)
g[0][0]=0;


若负无穷大 选择 int_min ,则会发生越界问题
负无穷大 选择 -0x3f3f3f3f( 0x3f3f3f3f 为int的最大值的一半)\

完整代码

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
       int n=prices.size();
       //将k进行优化
       k=min(k,n/2);
         //f 表示买入状态 g表示卖出状态
       //有[0,k-1] 笔交易
       vector<vector<int>>f(n,vector<int>(k+1,-0x3f3f3f));  
       vector<vector<int>>g(n,vector<int>(k+1,-0x3f3f3f));

       //初始化
       f[0][0]=-prices[0];
       g[0][0]=0;
       int i=0;
       int j=0;
       for(i=1;i<n;i++)
       {
           for(j=0;j<=k;j++)
           {
              f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i]);
            //修改后的状态转移方程
              g[i][j]=g[i-1][j];
              if(j-1>=0)
              {
                  g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
              }
           }
       }
       //寻找g的最后一行的最大值
       int ret=INT_MIN;
       for(j=0;j<=k;j++)
       {
           if(ret<g[n-1][j])
           {
               ret=g[n-1][j];
           }
       }
       //返回g的最后一行的最大值
       return ret;
    }
};

若有20天, k(交易次数)为30,
而最多交易只有10次,所以将k进行优化
k=min(k,n/2);
(n/2 代表最多进行交易的次数)

53. 最大子数组和

点击查看:最大子数组和


状态转移方程

将以i为结尾的所有子数组拿到,如:i位置处本身、i与i-1位置结合、i与i-2位置结合、i与下标0位置处结合 等生成的子数组
取其中和最大的那个

dp[i]:表示以i位置元素为结尾的所有子数组中的最大和


dp[i]可被划分为两类 :

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


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

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

初始化

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

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

若为dp[1],dp[1]=max(nums[1],dp[0]+nums[1])
为了保证不干扰结果,则将dp[0]的值置为0

完整代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
       int n=nums.size();
       //dp作为扩展数组 所以比原数组大1
       vector<int>dp(n+1,0);

       int i=0;
       //因为dp表可能都为负,所以初始值为最小值
       int ret=INT_MIN;
       for(i=1;i<n+1;i++)
       {
           //当前下标i作为扩展数组dp的下标 
           //想要使用dp下标 找到对应 原数组nums对应值
           //应该使用i-1
           dp[i]=max(nums[i-1],dp[i-1]+nums[i-1]);
           //寻找dp表的最大值
           if(ret<dp[i])
           {
               ret=dp[i];
           }
       }

       return ret;

    }
};

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

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

相关文章

vhost-net-原理-初始化流程-数据传输流程

文章目录 1.vhost net2.vhost-net的初始化流程vhost net设置vhost dev设置vhost vring设置 3.数据收发流程分析3.1 数据发送3.2 数据接收 4ioventfd和irqfd的通知机制4.1ioeventfdqemu侧kvm侧总体效果 4.2irqfdqemu侧kvm侧总体效果 参考&#xff1a; 1.vhost net 传统的virtio…

深入理解Java虚拟机jvm-垃圾收集器日志参数

垃圾收集器日志参数 查看GC基本信息查看GC详细信息查看GC前后的堆、方法区可用容量变化查看GC过程中用户线程并发时间以及停顿的时间查看收集器Ergonomics机制&#xff08;自动设置堆空间各分代区域大小、收集目标等内容&#xff0c;从Parallel收集器开始支持&#xff09;自动调…

windows下nginx location root路径问题

Windows版本nginx的多级路径问题 windows 下 nginx location root路径为多级目录的问题解决方案&#xff1a;使用双反斜杠--\\Windows版本的nginx常用命令一览&#xff1a; windows 下 nginx location root路径为多级目录的问题 nginx的windows版本中location路径为多级目录 需…

Git客户端:Tower for Mac 注册版

Tower是一款Mac OS X系统上的Git客户端软件&#xff0c;它提供了丰富的功能和工具&#xff0c;帮助用户更加方便地管理和使用Git版本控制系统。以下是Tower的一些特点&#xff1a; 1. 界面友好&#xff1a;Tower的界面友好&#xff0c;使用户能够轻松地掌握软件的使用方法。 …

【vs2022】MSVCRTD.lib 4098: 默认库“libcmtd.lib”与其他库的使用冲突

verbose-print- 整理&#xff1a;warning LNK4098: 默认库“LIBCMT”与其他库的使用冲突&#xff1b;请使用 /NODEFAULTLIB:library 讲解的非常详细&#xff1a; Linker 加入命令行选项 To set this linker option in the Visual Studio development environment Open the p…

Chapter 1: Introduction - Why Program? | Python for Everybody 讲义_En

文章目录 Python for Everybody课程简介Python for Everybody (Why Program?)Why should you learn to write programs?Creativity and motivationComputer hardware architectureUnderstanding programmingWords and sentencesConversing with PythonTerminology: Interpret…

MyBatis全篇

文章目录 MyBatis特性下载持久化层技术对比 搭建MyBatis创建maven工程创建MyBatis的核心配置文件创建mapper接口创建MyBatis的映射文件测试功能加入log4j日志功能加入log4j的配置文件 核心配置文件的完善与详解MyBatis的增删改查测试功能 MyBatis获取参数值在IDEA中设置中配置文…

Java Web HTMLCSS(2)23.6.28

2&#xff0c;CSS 2.1 概述 CSS 是一门语言&#xff0c;用于控制网页表现。我们之前介绍过W3C标准。W3C标准规定了网页是由以下组成&#xff1a; 结构&#xff1a;HTML表现&#xff1a;CSS行为&#xff1a;JavaScript CSS也有一个专业的名字&#xff1a;Cascading Style Sh…

Springboot 如何自动上传秒杀商品数据到Redis中上架商品

一、概述 如下图秒杀活动&#xff1a; 在这个秒杀活动中&#xff0c;需要自动上架一定时间段的商品&#xff0c;我们如何实现自动上传呢&#xff1f; 我们可以通过定时任务来实现的。在秒杀活动开始前&#xff0c;需要将商品信息存储到数据库中&#xff0c;并设置好库存和价格…

Linux学习之进程的通信方式信号:kill命令

kill -l可以看到可以使用信号量。 把下边的内容使用编辑器&#xff0c;比如vim写到./a.sh。 #!/bin/bashecho $$ while : # 无限循环 do: donecat a.sh看一下文件里边的内容。 chmod ur,ux a.sh给当前用户赋予a.sh文件的写和执行权限。 在第一个端口里边&#xff0c;使用./a…

在 TypeScript 中有效地使用 keyof 和 typeof 来表示类型

在本文中&#xff0c;我们将学习如何通过组合类型运算符和枚举来提取和声明常量类型typeof&#xff0c;以使您的代码库得到优化。keyof 先决条件 为了获得更好的编码体验&#xff0c;您应该在 IDE 中安装 TypeScript&#xff0c;例如VSCode。它将为您提供许多基本功能&#xff…

Linux——进程通信之共享内存

目录 一. 回顾上文 二.共享内存 1.定义 2.特点&#xff1a; 3.实现步骤&#xff1a; 如下为成功链接共享内存使用权的完整步骤&#xff1a; 4.函数介绍 4.1shmget函数 4.1.2参数介绍 4.2ftok函数&#xff1a; 4.2.1参数介绍 关于ftok(); shmget();函数的代码实验…

基于当量因子法、InVEST、SolVES模型等多技术融合在生态系统服务功能社会价值评估中的应用

查看原文>>>基于当量因子法、InVEST、SolVES模型等多技术融合在生态系统服务功能社会价值评估中的应用及论文写作、拓展分析 本文将讲述用于评估生态系统服务价值的当量因子法、InVEST模型、SolVES模型及其原理&#xff0c;您将学会三种模型的原理与运行方法&#xf…

基于Docker的JMeter分布式压测

目录 前言&#xff1a; Docker Docker在JMeter分布式测试中的作用 Dockerfile用于JMeter基础&#xff1a; Dockerfile for JMeter Server / Slave: 总结 前言&#xff1a; 基于Docker的JMeter分布式压测是一种将JMeter测试分布在多个容器中进行的方法&#xff0c;可以提高…

《计算机系统2》学习笔记

目录 计算机系统漫游 Amdahl定理 信息的表示和处理 信息存储 进制转化 小端法 大端法 布尔代数 位级运算 逻辑运算 移位运算 整数表示 无符号数编码 补码编码 有符号数和无符号数之间的转换 扩展数的位表示 截断数字 整数运算 无符号加法 无符号数求反 有…

信号链噪声分析5

目录 概要 整体架构流程 技术名词解释 技术细节 小结 概要 提示&#xff1a;这里可以添加技术概要 残余相位噪声测量法消除了外部噪声源&#xff08;例如电源或输入时钟&#xff09;的影响&#xff0c;而绝对相位 噪声测量法包含了这些来源的噪声。残余相位噪声装置可以隔离并…

Upload靶场通关笔记

文章目录 一、Pass-011.抓包上传2.获取上传路径3.工具验证 二、Pass-02三、Pass-031.使用httpd.conf自定义后缀2.提取上传文件名3.工具测试4.注意点四、Pass-041.上传.htaccess2.上传图片3.工具测试 五、Pass-05六、Pass-061.空格.号绕过2.工具测试 七、Pass-07八、Pass-081.特…

联想黄莹:6G将是全智能应用下连接虚拟与现实世界的“超级通道”

6月28日&#xff0c;以“时不我待”为主题的MWC上海世界移动通信大会正式开幕。在当天下午举办的“6G愿景及关键推动力”大会论坛上&#xff0c;联想集团副总裁、联想研究院5G实验室负责人黄莹博士发表了“共铸辉煌&#xff1a;对6G技术和应用的思考与展望”主题演讲。他认为&a…

STM32F407 GPIO口输出配置配置步骤

STM32F407ZGT6 是意法半导体&#xff08;STMicroelectronics&#xff09;公司推出的一款高性能ARM Cortex-M4核心的32位微控制器&#xff08;MCU&#xff09;。它是 STM32F4 系列的一员&#xff0c;具备强大的处理能力和丰富的外设功能&#xff0c;适用于各种应用领域。 【1】…

3.6.6.异步SIGIO : fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN) 3.6.7.存储映射IO

3.6.6.异步IO &#xff1a;SIGIO 3.6.6.1、何为异步IO (1)几乎可以认为&#xff1a;异步IO就是操作系统用软件实现的一套中断响应系统。 (2)异步IO的工作方法是&#xff1a;我们当前进程注册一个异步IO事件&#xff08;使用signal注册一个信号SIGIO的处理函数&#xff09;&…