动态规划课堂3-----简单多状态问题(买卖股票最佳时机)

news2024/11/15 2:02:19

目录

引入:

例题1:按摩师(打家劫舍I)

例题2:打家劫舍II

例题3:删除并获得点数

例题4:粉刷房子

例题5:买卖股票的最佳时机含冷冻

结语:


引入:

相信看到这里的友友们对动态规划已经有了一定的了解,下面我将介绍动态规划的简单多状态dp问题。所谓多状态就是在一步下有不同的情况要区分(例如买股票,今天可以分为买还是不买的多两种情况)。由于算法只讲知识点是远远不够的故下面我会在例题中穿插知识点帮助理解。动态规划一般的解题步骤还是1. 状态表示,2.状态转移方程,3.初始化,4.填表顺序,5.返回值。在写代码时一定要把这5步考虑清楚再写代码。

例题1:按摩师(打家劫舍I)

链接:按摩师

题目简介:

一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

解法(动态规划):

1. 状态表示:

对于简单的线性dp ,我们可以用「经验+题⽬要求」来定义状态表示例如:

(1)以某个位置为结尾的最小花费。

(2)以某个位置为起点到终点的最小花费。

故我们这题采用常用的方式:dp[i] 表⽰:选择到i 位置时,此时的最⻓预约时⻓。由于我们这个题在i 位置的时候,会⾯临选择或者不选择两种抉择,所依赖的状态需要细分。

下面之所以用f和g是因为高中我们所学的f(x)和g(x),可以换成其他的(但最好用f和g就当作是不成文的规定,这样代码的可读性会更高)。

(1)f[i] 表示:选择到i 位置时, nums[i] 必选,此时的最⻓预约时⻓。

(2)g[i]表示:选择到i 位置时, nums[i] 不选,此时的最⻓预约时⻓。

2.状态转移方程

因为状态表示定义了两个,因此我们的状态转移⽅程也要分析两个:

对于f[i] :

如果nums[i] 必选,那么我们仅需知道i - 1 位置在不选的情况下的最⻓预约时⻓, 然后加上nums[i] 即可,因此f[i] = g[i - 1] + nums[i] 。

对于g[i] :

如果nums[i] 不选,那么i - 1 位置上选或者不选都可以。因此,我们需要知道i - 1 位置上选或者不选两种情况下的最⻓时⻓,因此g[i] = max(f[i - 1], g[i - 1]) 。

在状态转移方程这里可以画图来帮助我们理解

3.初始化

这道题的初始化⽐较简单,因此⽆需加辅助节点(前两篇文章已解释),仅需初始化f[0] = nums[0], g[0] = 0 即可。

4.填表顺序

根据「状态转移⽅程」得「从左往右,两个表⼀起填」。

5.返回值

根据「状态表示」,应该返回max(f[n - 1], g[n - 1]) 。

代码实现如下:

class Solution {
    public int massage(int[] nums) {
        //1.创建dp表
        //2.初始化
        //3.填表
        //4.返回值
        int n = nums.length;
        if(n == 0){
            return 0;
        }
        int[] f = new int[n];//选择
        int[] g = new int[n];//不选择
        f[0] = nums[0];
        for(int i = 1;i < n;i++){
            f[i] = g[i - 1] + nums[i];
            g[i] = Math.max(g[i - 1],f[i - 1]);
        }
        return Math.max(g[n -1],f[n - 1]);
    }
}

时间复杂度:O(n)

空间复杂度:O(n)

例题2:打家劫舍II

链接:打家劫舍II

题目简介:

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

 解法(动态规划):

通过阅读题目友友可以发现这题和例题一是差不多的,唯一不同就是例题二加了一个末尾和开头的限制。上⼀个问题是⼀个「单排」的模式,这⼀个问题是⼀个「环形」的模式,也就是⾸尾是相连的。但是我们可以将「环形」问题转化为「两个单排」问题:

(1)偷第⼀个房屋时的最大⾦额x ,此时不能偷最后⼀个房⼦,因此就是偷[0, n - 2] 区间的房⼦。

(2)不偷第⼀个房屋时的最大⾦额y ,此时可以偷最后⼀个房⼦,因此就是偷[1, n - 1] 区 间的房⼦。

两种情况下的「最大值」,就是最终的结果。

代码如下:

下面的rob1方法就是例题一。

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        return Math.max(nums[0] + rob1(nums,2,n - 2), rob1(nums,1,n - 1));
    }
    public int rob1(int[] nums,int left,int right){
        if(left > right){
            return 0;
        }
        //1.创建dp表
        //2.初始化
        //3.填表
        //4.返回值
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        f[left] = nums[left];
        for(int i = left + 1;i <= right;i++){
            f[i] = g[i - 1] + nums[i];
            g[i] = Math.max(g[i - 1],f[i - 1]);
        }
        return Math.max(f[right],g[right]);
    }
}

时间复杂度:O(n) 

空间复杂度:O(n)

例题3:删除并获得点数

链接:删除并获得点数

题目简介:

给你一个整数数组 nums ,你可以对它进行一些操作。

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。

开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

解法(动态规划):

其实这道题依旧是「打家劫舍I」问题的变型。 我们注意到题⽬描述,选择x 数字的时候, x - 1 与x + 1 是不能被选择的。像不像「打家劫舍」问题中,选择i 位置的⾦额之后,就不能选择i - 1 位置(数组中)以及i + 1 位置的⾦额呢~ 因此,我们可以创建⼀个⼤⼩为10001 (根据题⽬的数据范围)的hash 数组,将nums 数 组中每⼀个元素x ,累加到hash 数组下标为x 的位置处,然后在hash数组上来⼀次「打家劫舍」即可。

过程如下图:

弧线表示0到i - 1之间能获得的最大点数。

代码如下:

class Solution {
    public int deleteAndEarn(int[] nums) {
        //1.创建dp表
        //2.初始化
        //3.填表
        //4.返回值
        int n = 10001;
        int[] arr = new int[n];
        for(int x:nums){
            arr[x] += x;
        }
        int[] f = new int[n];
        int[] g = new int[n];
        f[0] = arr[0];
        for(int i = 1;i < n;i++){
            f[i] = g[i - 1] + arr[i];
            g[i] = Math.max(f[i - 1],g[i - 1]);
        }
        return Math.max(f[n - 1],g[n - 1]);
    }
}

时间复杂度:O(n) 

空间复杂度:O(n)

例题4:粉刷房子

链接:粉刷房⼦

题目简介:

 解法(动态规划):

 1. 状态表示:

对于线性dp ,我们可以⽤「经验+题⽬要求」来定义状态表⽰:但是我们这个题在i 位置的时候,会⾯临「红」「蓝」「绿」三种抉择,所依赖的状态需要细分:

(1)dp[i][0] 表⽰:粉刷到i 位置的时候,最后⼀个位置粉刷上「红⾊」,此时的最⼩花费。 

(2)dp[i][1] 表⽰:粉刷到i 位置的时候,最后⼀个位置粉刷上「蓝⾊」,此时的最⼩花费。

(3)dp[i][2] 表⽰:粉刷到i 位置的时候,最后⼀个位置粉刷上「绿⾊」,此时的最⼩花费。

 2.状态转移方程

因为状态表⽰定义了三个,因此我们的状态转移⽅程也要分析三个:

对于dp[i][0] :

如果第i个位置粉刷上「红⾊」,那么i-1位置上可以是「蓝⾊」或者「绿⾊」。因此我们 需要知道粉刷到i-1位置上的时候,粉刷上「蓝⾊」或者「绿⾊」的最⼩花费,然后加上i 位置的花费即可。于是状态转移⽅程为: dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + costs[i - 1][0] ;

同理,我们可以推导出另外两个状态转移⽅程为:

dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1] ;

dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i - 1][2] 。

 3.初始化

采用最前⾯加上⼀个「辅助结点」。

注意点:(1)辅助结点⾥⾯的值要「保证后续填表是正确的」(2)「下标的映射关系」。

 4.填表顺序

 5.返回值

根据「状态表⽰」,应该返回最后⼀个位置粉刷上三种颜⾊情况下的最⼩值,因此需要返回: min(dp[n][0], min(dp[n][1], dp[n][2])) 。

代码如下:

class Solution {
    public int minCost(int[][] costs) {
        //1.创建dp表
        //2.初始化
        //3.填表
        //4.返回值
        int n = costs.length;
        int[][] dp = new int[n + 1][3];
        for(int i = 1;i <= n;i++){
            dp[i][0] = Math.min(dp[i - 1][1],dp[i - 1][2]) + costs[i - 1][0];
            dp[i][1] = Math.min(dp[i - 1][0],dp[i - 1][2]) + costs[i - 1][1];
            dp[i][2] = Math.min(dp[i - 1][1],dp[i - 1][0]) + costs[i - 1][2];
        }
        return Math.min(dp[n][0],Math.min(dp[n][1],dp[n][2]));
    }
}

时间复杂度:O(n) 

空间复杂度:O(n)

例题5:买卖股票的最佳时机含冷冻

链接:买卖股票的最佳时机含冷冻期

题目简介:

  解法(动态规划):

 1. 状态表示:

这⾥我们选择⽐较常⽤的⽅式,以某个位置为结尾,结合题⽬要求,定义⼀个状态表⽰:由于有「买⼊」「可交易」「冷冻期」三个状态,因此我们可以选择⽤三个数组,其中:

(1)dp[i][0] 表⽰:第i 天结束后,处于「买⼊」状态,此时的最⼤利润。

(2)dp[i][1] 表⽰:第i 天结束后,处于「可交易」状态,此时的最⼤利润。

(3)dp[i][2] 表⽰:第i 天结束后,处于「冷冻期」状态,此时的最⼤利润。

我们要谨记规则:

(1)处于「买⼊」状态的时候,我们现在有股票,此时不能买股票,只能继续持有股票,或者卖 出股票;

(2)处于「买⼊」状态的时候,我们现在有股票,此时不能买股票,只能继续持有股票,或者卖 出股票;

 2.状态转移方程

确定状态表示后,我们可以画图来帮助我们理解根据题目要求我们可以画图下图,并推出状态转移方程。

 3.初始化

三种状态都会⽤到前⼀个位置的值,因此需要初始化每⼀⾏的第⼀个位置:

dp[0][0] :此时要想处于「买⼊」状态,必须把第⼀天的股票买了,因此dp[0][0] = - prices[0] ; dp[0][1] :啥也不⽤⼲即可,因此dp[0][1] = 0 ;

dp[0][2] :手上没有股票,买⼀下卖⼀下就处于冷冻期,此时收益为0 ,因此 dp[0][2] = 0 。

4.填表顺序

根据「状态表⽰」,我们要三个表⼀起填,每⼀个表「从左往右」。

5.返回值

应该返回「卖出状态」下的最⼤值,因此应该返回max(dp[n - 1][1], dp[n - 1] [2]) 。

代码如下:

class Solution {
    public int maxProfit(int[] prices) {
        //1.创建dp表
        //2.初始化
        //3.填表
        //4.返回值
        int n = prices.length;
        int[][] dp = new int[n][3];
        dp[0][0] = -prices[0];
        for(int i = 1;i < n;i++){
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }
        return Math.max(dp[n - 1][1],dp[n - 1][2]);
    }
}

时间复杂度:O(n) 

空间复杂度:O(n)

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

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

相关文章

PowerBI怎么修改数据库密码

第一步&#xff1a;点击转换数据 第二步&#xff1a;点击数据源设置 第三步&#xff1a;点击编辑权限 第四步&#xff1a;点击编辑 第五步&#xff1a;输入正要修改的密码就可以了

c++中使用lambda表达式的作用和用法

lambda表达式&#xff1a; 这是C11引入的一种新特性&#xff0c;它可以让您在需要定义函数对象的地方&#xff0c;直接编写一个匿名的、可以捕获上下文变量的函数体&#xff0c;非常适合用作回调函数、临时计算或定义小型函数对象。 lambda表达式与普通函数类似&#xff0c;也有…

linux系统如何安装nginx

首先下载nginx安装包 wget -c http://nginx.org/download/nginx-1.23.1.tar.gz然后解压安装包 tar -zxvf nginx-1.23.1.tar.gz如果服务器没有wget&#xff0c;可以安装一下&#xff0c;有的话可以跳过 yum install -y wget 然后安装相关依赖 yum install -y gcc-c zlib zl…

pdf.js使用步骤

使用pdfjs 网页在线预览需要后端服务器支持 1、下载PDF.js 源码包 地址&#xff1a;PDF.js 2、解压源码包&#xff0c;将源码包放置到后端服务器 3、后端部署完成后 访问 viewer.html 类似上图 4、访问在线pdf文件 http://localhost:8081/web/viewer.html?filexxxx.pdf …

超好用的自动化测试工具——Python脚本

前言 ⾃动化测试会越来越受到重视 在移动互联⽹时代&#xff0c;对于质量的要求⽐PC时代⾼的多&#xff0c;⽽投⼊产出⽐最⾼的⾃动化测试&#xff0c;将会是⼤部分公司的⾸选⽅向&#xff0c;但需要严格掌握⼀门语⾔。 快速、高效的测试工具及脚本&#xff0c;能使程序猿的…

Docker技术概论(4):Docker CLI 基本用法解析

Docker技术概论&#xff08;4&#xff09; Docker CLI 基本用法解析 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:http…

2024腾讯云服务器优惠价格表又降价了,给同行干emo了

腾讯云优惠活动2024新春采购节活动上线&#xff0c;云服务器价格已经出来了&#xff0c;云服务器61元一年起&#xff0c;配置和价格基本上和上个月没什么变化&#xff0c;但是新增了8888元代金券和会员续费优惠&#xff0c;腾讯云百科txybk.com整理腾讯云最新优惠活动云服务器配…

C++条件变量

概述 条件变量的使用&#xff0c;其实是一种多线程通知模式&#xff0c;当线程一使用完数据后&#xff0c;通过条件变量通知其他线程&#xff0c;C11后开始支持。 说明 条件变量必须配合mutex使用&#xff0c;确保并发访问的排他性 std::unique_lock<std::mutex> loc…

文献笔记:LINE: Large-scale Information Network Embedding

paper 看完√ code复现ing https://arxiv.org/pdf/1503.03578v1.pdf 本文研究了将非常大的信息网络嵌入到低维向量空间的问题&#xff0c;这在可视化、节点分类和链路预测等许多任务中都很有用。大多数现有的图形嵌入方法无法扩展到通常包含数百万个节点的现实世界信息网络。…

【C语言】指针初阶2.0版本

这篇博文我们来继续学习指针的其他内容 指针2.0 传值调用与传址调用传值调用传址调用 一维数组与指针理解数组名使用指针深入理解一维数组 二级指针指针数组二维数组与指针 传值调用与传址调用 在开始之前&#xff0c;我们需要先了解这个概念&#xff0c;后面才能够正常的学习…

成功解决‘OpenpyxlWriter’ object has no attribute ‘save’

成功解决‘OpenpyxlWriter’ object has no attribute ‘save’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到…

在vue2中使用饼状图

1.引入vue2和echarts <script src"https://cdn.jsdelivr.net/npm/vue2.7.14/dist/vue.js"></script> <script src"https://cdn.jsdelivr.net/npm/echarts5.4.0/dist/echarts.min.js"></script> 2.1 补充基本的body内容 <div id…

在UniApp中引入大于40kb字体包的记录

因为项目UI需要特殊字体&#xff0c;所以给了一个80kb字体包&#xff0c;但是在正常的使用导入时候发现不生效 这是我的导入过程 1.把下载好的文件放入static/font目录中 2.在app.vue中引用 font-face { font-family: zitiming; src: url(/static/font/YouSheBiaoTiHei-2.t…

C++ //练习10.3 用accumulate求一个vector<int>中的元素之和。

C Primer&#xff08;第5版&#xff09; 练习 10.3 练习10.3 用accumulate求一个vector中的元素之和。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*******************************************************************…

【Pytorch】成功解决AttributeError: ‘tuple’ object has no attribute ‘dim’

【Pytorch】成功解决AttributeError: ‘tuple’ object has no attribute ‘dim’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&…

HTML---表单验证

文章目录 目录 本章目标 一.表单验证概述 二.表单选择器 属性过滤选择器 三.表单验证 表单验证的方法 总结 本章目标 掌握String对象的用法会使用表单选择器的选择页面元素会使用JQuery事件进行表单验证Ajax的概念和作用 一.表单验证概述 前端中的表单验证是在用户提交表…

《Spring Security 简易速速上手小册》第5章 高级认证技术(2024 最新版)

文章目录 5.1 OAuth2 和 OpenID Connect5.1.1 基础知识详解OAuth2OpenID Connect结合 OAuth2 和 OIDC 5.1.2 重点案例&#xff1a;使用 OAuth2 和 OpenID Connect 实现社交登录案例 Demo 5.1.3 拓展案例 1&#xff1a;访问受保护资源案例 Demo测试访问受保护资源 5.1.4 拓展案例…

HCIA-Datacom实验指导手册:7 构建简单 IPv6 网络

HCIA-Datacom实验指导手册&#xff1a;7 构建简单 IPv6 网络 一、实验介绍&#xff1a;二、实验拓扑&#xff1a;三、实验目的&#xff1a;四、配置步骤&#xff1a;步骤 1 设备基础配置设备命名 步骤 2 配置设备及接口 IPv6 功能步骤 3 配置接口的 link-local 地址&#xff0c…

c++数据结构算法复习基础-- 2 -- 线性表-单链表-常用操作接口-复杂度分析

1、链表 特点 每一个节点都是在堆内存上独立new出来的&#xff0c; 节点内存不连续优点 内存利用率高&#xff0c;不需要大块连续内存 插入和删除节点不需要移动其它节点&#xff0c;时间复杂度O(1)。 不需要专门进行扩容操作缺点 内存占用量大&#xff0c;每一个节点多出存…

使用 MongoDB Atlas 无服务器实例更高效地开发应用程序

使用 MongoDB Atlas无服务器实例更高效地开发应用程序 身为开发者&#xff0c;数据库并不一定需要您来操心。您可不想耗费时间来预配置集群或调整集群大小。同样地&#xff0c;您也不想操心因未能正确扩展而导致经费超标。 MongoDB Atlas 可为您提供多个数据库部署选项。虽然…