动态规划系列问题之打家劫舍和买股票

news2024/11/16 9:27:50

动态规划系列问题

  • 1.打家劫舍问题
    • 1.1打家劫舍I
    • 1.2打家劫舍II
    • 1.3打家劫舍III
  • 2.买股票问题
    • 2.1买股票的最佳时机
    • 2.2买股票的最佳时机II
    • 2.3买股票的最佳时机III
    • 2.4买股票的最佳时机IV
    • 2.5买卖股票的最佳时机含冷冻期
    • 2.6买卖股票的最佳时机含手续费

题目解析参考了代码随想录

https://www.programmercarl.com/

1.打家劫舍问题

1.1打家劫舍I

在这里插入图片描述
这个题目比较简单,我们每一次递推的时候,只需要考虑选不选当前房子就行

dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])(两种情况选最大值即可)
dp[i - 1]表示不选当前房子得到的价值,dp[i - 2] + nums[i]表示选了当前房子得到的价值

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size() == 1) return nums[0];
        vector<int> dp(nums.size());
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);
        for(int i = 2; i < nums.size(); i++){
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[nums.size() - 1];
    }
};

1.2打家劫舍II

在这里插入图片描述这个题目在上一个题目的基础上,将房屋首尾相连了,这个时候的解决方法也很简单,我们动态规划的时候分两种情况考虑,dp1是选择了打劫了第一家房子,dp2是没有选择打劫第一家房子,递推式和上面相同,但是这里dp1和dp2数组的初始化有讲究

dp1[0] = nums[0] ,dp[1] = nums[0] 因为打劫了第一家房子所有不能选择第二家房子和最后一家房子
dp2[0] = 0, dp2[1] = nums[1] 打劫了第二家房子,第一家房子不能后选择,但是最后一间房子可以考虑打劫

代码实现如下:

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(n == 1) return nums[0];
        if(n == 2) return max(nums[0], nums[1]);
        //nums围成了一个圈,所以我们这个时候的dp就可以分两种情况考虑:偷第一户人,和不偷第一户人
        vector<int> dp1(n), dp2(n);
        //dp1偷第一户人家:那么它不可以偷最后一户人家(所以i<n - 1)
        dp1[0] = dp1[1] = nums[0];
        for(int i = 2; i < n - 1; i++){
            dp1[i] = max(dp1[i - 2] + nums[i], dp1[i - 1]);
        }
        //dp2不偷第一户人家:那么就可以选择是否偷最后一户人家(所以i<n)
        dp2[0] = 0, dp2[1] = nums[1];
        for(int i = 2; i < n; i++){
            dp2[i] = max(dp2[i - 2] + nums[i], dp2[i - 1]);
        }
        return max(dp1[n - 2], dp2[n - 1]);
    }
};

1.3打家劫舍III

在这里插入图片描述
小偷打劫也真的是难啊,从数组变成了二叉树,添加了二叉树遍历的考点,难度又提升了。给出二叉树题目的常见题解,就是暴力递归,得到所有可能结果,为了不超时,暴力递归的时候添加上记忆化的操作。二叉树遍历选择后序遍历,因为是否选择根节点的值取决于其子节点的放返回结果(要想办法选择最大价值)

解法一:递归+记忆化

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    unordered_map<TreeNode* ,int> umap; //记录计算过的结果
    //选择后序遍历,需要通过递归函数的返回值来做下一步计算
    int rob(TreeNode* root) {
        if(root == NULL) return 0;
        if(root->left == nullptr && root -> right == nullptr) return root->val;
        if(umap[root]) return umap[root];//如果umap里面有记录了,就直接返回
        //偷父节点
        int val1 = root->val;
        if(root->left) val1 += rob(root->left->left) +rob(root->left->right);
        if(root->right) val1 += rob(root->right->left) + rob(root->right->right);
        //不偷父节点的情况
        int val2 = rob(root->left) + rob(root->right);
        umap[root] = max(val1, val2);
        return max(val1, val2);
    }
};

那么动态规划如何实现该题目的求解呢?这里定义dp数组就变得尤为重要了,在本题中,动态规划要结合递归的特性来定义dp,dp为一个大小为2的数组

在这里插入图片描述

确定状态转移方程

在这里插入图片描述

方法二:动态规划代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int rob(TreeNode* root) {
        vector<int> result = robTree(root);
        return max(result[0], result[1]);
    }
    //长度为2的数组,0:不偷,1:偷
    vector<int> robTree(TreeNode* cur){
        if(cur == NULL) return vector<int>{0, 0};
        vector<int> left = robTree(cur->left);
        vector<int> right = robTree(cur->right);
        //偷cur,那么就不能偷左右节点
        int val1 = cur->val + left[0] + right[0];
        //不偷cur,那么可以偷也可以不偷左右节点,取最大的情况
        int val2 = max(left[0], left[1]) + max(right[0], right[1]);

        return {val2, val1};
    }
};

2.买股票问题

2.1买股票的最佳时机

在这里插入图片描述这个题目,我们可以这样:如果第二天卖出去为正收入,那么就继续考虑第三天再买…如果第二天卖出去为负,那么就更新买入的位置…这个过程中,用一个ans维护卖出获得的最大值

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans = 0;
        int cnt = 0;
        if(prices.size() == 1) return ans;
        for(int i = 1; i < prices.size(); i++){
            cnt += prices[i] - prices[i - 1];
            if(cnt < 0){
                cnt = 0;
            }
            ans = max(cnt, ans);
        }
        return ans;
    }
};

2.2买股票的最佳时机II

在这里插入图片描述既然可以多次买入卖出,那么我们只要买入股票第二天卖出是获利的那么就是把利润添加到结果中,只要两天只差(卖出-买入 >0)就买,然后把利润一直++,这样就可以获得最大利润

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans = 0;
        if(prices.size() == 1) return ans;
        for(int i = 1; i <  prices.size(); i++){
            int gap = prices[i] - prices[i - 1];
            if(gap > 0) ans += gap;
        }
        return ans;
    }
};

2.3买股票的最佳时机III

在这里插入图片描述
当我们只能够买入两次,并且卖出两次(且买入卖出不可以交叉时),我们动态规划的状态如下:

状态定义

在这里插入图片描述

状态转移方程

在这里插入图片描述

class Solution {
public:
    //动态规划过程实现过程
    //动态规划过程分为下面四个状态:不持有股票;第一次持有股票;第一次不持有股票;第二次持有股票;第二次不持有股票
    int maxProfit(vector<int>& prices) {
        if(prices.size() == 0) return 0;
        vector<vector<int>> dp(prices.size(), vector<int>(5, 0));
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for(int i = 1; i < prices.size(); i++){
            dp[i][0] = dp[i - 1][0];//不持有股票
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);//第一次持有股票
            dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]);//第一次卖出股票
            dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);//第二次持有股票
            dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);//第二次卖出股票
        }
        return dp[prices.size() - 1][4];
    }
};

2.4买股票的最佳时机IV

在这里插入图片描述当买卖次数由上一题的2两次(买卖至多各两次)变成本题目的k次,我们需要发现交易的规律

定义动态规划数组

在这里插入图片描述

递推方程式

在这里插入图片描述

代码实现

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if(prices.size() == 0) return 0;
        //奇数次是买入,偶数次是卖出
        vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
        for(int i = 0; i < k; i++){
            dp[0][2 * i + 1] = -prices[0];
        }
        for(int i = 1; i < prices.size(); i++){
            for(int j = 0; j < k; j++){
                //每一次卖出和买入的状态都和上一次卖出买入的状态有关
                dp[i][2 * j + 1] = max(dp[i - 1][2 * j + 1], dp[i - 1][2 * j] - prices[i]);
                dp[i][2 * (j + 1)] = max(dp[i - 1][2 * j + 1] + prices[i], dp[i - 1][2 * (j + 1)]);
            }
        }
        return dp[prices.size() - 1][2 * k];
    }
};

2.5买卖股票的最佳时机含冷冻期

在这里插入图片描述

状态的划分

在这里插入图片描述

代码实现

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if(n == 0) return 0;
        vector<vector<int>> dp(n, vector<int>(4, 0));
        dp[0][0] -= prices[0];//持有股票
        //0:持有股票状态(今天买入的或者是之前就买入了一直没动)
        //1:保持卖出股票的状态(两天前就卖出股票,度过了冷冻期,或者是前一天卖出了股票,一直没有操作)
        //2:今天卖出股票
        //3:今天为冷冻状态,但是冷冻期不可持续,只有一天
        for(int i = 1; i < n; i++){
            dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3] - prices[i], dp[i - 1][1] - prices[i]));
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
            dp[i][2] = dp[i - 1][0] + prices[i];
            dp[i][3] = dp[i - 1][2];
        }
        return max(dp[n - 1][3], max(dp[n - 1][1], dp[n - 1][2]));
    }
};

2.6买卖股票的最佳时机含手续费

在这里插入图片描述

自己的解法

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int buy = INT_MIN, sell = 0;
        for(int p : prices){
            buy = max(buy, sell - p - fee);
            sell = max(sell, buy + p);
        }
        return sell;
    }
};

代码随想录的实现分析方法

在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(2, 0));
        dp[0][0] -= prices[0]; // 持股票
        for (int i = 1; i < n; i++) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);
        }
        return max(dp[n - 1][0], dp[n - 1][1]);
    }
};

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

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

相关文章

【Redis漏洞利用总结】

前言 redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。Redis默认使用 6379 端口。 一、redis未授权访问漏洞 0x01 漏洞描述 描述: Redis是一套开源的使用ANSI C编写、支持网络、可基于内存…

基于深度学习的交通标志检测和识别(从原理-环境配置-代码运行)

基于深度学习的交通标志检测和识别是一种先进的计算机视觉技术&#xff0c;利用深度神经网络模型来准确地检测和识别道路上的各种交通标志。下面是对其介绍并分点阐述其重要性和应用场景&#xff1a; 1. 深度学习技术&#xff1a; 交通标志检测和识别利用深度学习技术&#x…

笔试面试题——二叉树进阶(二)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、二叉搜索树与双向链表1、题目讲解2、思路讲解递归展开图3、代码实现 二、从前序遍历和中序…

刷题 ------ 排序

文章目录 1.K 次取返后最大化的数组和&#xff08;堆&#xff09;2.数组的相对排序&#xff08;桶&#xff09;3.最小绝对差4.根据数字二进制下1的数目排序&#xff08;qsort&#xff09;5.有多少小于当前数字的数字6.非递增顺序的最小子序列7.按照频率将数组升序排序&#xff…

unity 2021 发布安卓版本,谷歌限流国内,报错CommandInvokationFailure: Gradle build failed

在使用 Unity 2022 打包安卓项目时&#xff0c;遇到 gradle 无法访问或下载超级慢最终超时出错的问题解决 解决方案&#xff1a; 1. 在 Build Settings 左下角打开 Player Settings&#xff0c;在 Android 平台图标选项卡下找到 Publishing Settings 区域 勾选 2. 从国内…

​《WebKit 技术内幕》学习之九(3): JavaScript引擎

3 JavaScriptCore引擎 3.1 原理 JavaScriptCore引擎是WebKit中的默认JavaScript引擎&#xff0c;也是苹果在开源WebKit项目之后&#xff0c;开源的另外一个重要的项目。同其他很多引擎一样&#xff0c;在刚开始的时候它的主要部分是一个基于抽象语法树的解释器&#xff0c;这…

【数据库原理】(38)数据仓库

数据仓库&#xff08;Data Warehouse, DW&#xff09;是为了满足企业决策分析需求而设计的数据环境&#xff0c;它与传统数据库有明显的不同。 一.数据库仓库概述 定义: 数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合&#xff0c;用于支持企业管理和…

成都爱尔胡建斌院长提醒视网膜脱离到底有多危险?!

视网膜脱离是视网膜神经上皮层与色素上皮层的分离。 视网膜脱离之危险&#xff0c;在与视网膜脱离后外层视网膜得不到脉络膜的血液供应&#xff0c;如不及时复位&#xff0c;视网膜感光细胞会发生凋亡&#xff0c;视力就不易恢复。 就症状来说&#xff0c;患者发病初期眼前多有…

0122-1-JavaScript高级程序设计11-27章

前言 通过阅读这本书写下的一些笔记 《JavaScript高级程序设计》 第11章——期约与异步函数 11.2 期约&#xff08;promise&#xff09;:是对 尚不存在结果 的一个替身。 /*** 期约与异步函数* 什么是Promise?* &#xff08;1&#xff09;从语法上来说&#xff1a;Promis…

np.argsort排序问题(关于位次)-含GitHub上在numpy项目下提问的回复-总结可行方案

np.argsort 与获取位相关问题 位次: 数组中的数据在其排序之后的另一个数组中的位置 [1,0,2,3] 中 0的位次是1 1的位次是2 2的位次是3 3的位次是4 这里先直接给出结论&#xff0c;np.argsort()返回的索引排序与实际位次在确实在某些情况下会出现一致&#xff0c;但后来numpy的开…

HubSpot SEO功能好用吗?

HubSpot对于SEO&#xff08;搜索引擎优化&#xff09;提供了全面的工具和功能&#xff0c;帮助用户优化其网站以在搜索引擎中取得更好的排名。以下是关于HubSpot SEO的一些关键方面&#xff1a; 内容优化&#xff1a; HubSpot的内容工具允许用户创建并优化吸引人的内容。通过关…

pikachu验证码绕过第三关攻略

打开pikachu靶场第三关&#xff1a; 挂上代理&#xff0c;随便输入账户密码&#xff1a; 返回bp。进行放包发现显示token错误。 每一次登录的返回包会带有token相关数据用于下一次的登录认证&#xff1a; 进行替换token值&#xff1a; 替换完成开始进行检点的爆破&#xff1a;…

s3fs挂载minio集群到本地目录

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 1. 前言 MinIO 是一款高性能的对象存储&#xff0c;与 Amazon S3 云存储服务兼容&#xff0c;并且号称是世界上最快的对象存储服…

LLM RAG 多种方式装载LLM的实践

一、大模型系统中检索增强生成&#xff08;RAG&#xff09;的意义 当前大模型在处理特定领域或者高度专业化的查询时表现出知识缺失&#xff0c;当所需信息超出模型训练数据范围或需要最新数据时&#xff0c;大模型可能无法提供准确答案。基于行业SOP、行业标准、互联网实时信…

中间件-缓存、索引、日志

文章目录 缓存中间件本地缓存中间件分布式缓存中间件全文索引中间件分布式日志中间件小结 缓存中间件 缓存是性能优化的一大利器 我们先一起来看一个用户中心查询用户信息的基本流程 这时候&#xff0c;如果查找用户信息这个 API 的调用频率增加&#xff0c;并且在整个业务流…

强化学习(四)动态规划——1

动态规划算法&#xff08;DP&#xff09;&#xff1a;在马尔可夫决策过程&#xff08;MDP&#xff09;的完美环境模型下计算最优策略。但其在强化学习中实用性有限&#xff0c;其一是它是基于环境模型已知&#xff1b;其二是它的计算成本很大。但它在理论伤仍然很重要&#xff…

Vscode 顶部Menu(菜单)栏消失如何恢复

Vscode 顶部Menu(菜单)栏消失如何恢复&#xff1f; 首先按一下 Alt按键&#xff0c;看一下是否恢复了菜单栏如果恢复了想了解更进一步的设置&#xff0c;或是没能恢复菜单栏&#xff0c;可以看后续。 1.首先点击左下角 齿轮&#xff0c;打开settings; 或者 直接 ctrl 逗号 …

如何本地搭建Splunk Enterprise数据平台并实现任意浏览器公网访问

文章目录 前言1. 搭建Splunk Enterprise2. windows 安装 cpolar3. 创建Splunk Enterprise公网访问地址4. 远程访问Splunk Enterprise服务5. 固定远程地址 前言 本文主要介绍如何简单几步&#xff0c;结合cpolar内网穿透工具实现随时随地在任意浏览器&#xff0c;远程访问在本地…

java SSM项目预算生成管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM项目预算生成管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的 源代码和数据库&#xff0c;系统主…

《WebKit 技术内幕》学习之十(1): 插件与JavaScript扩展

虽然目前的浏览器的功能很强 &#xff0c;但仍然有其局限性。早期的浏览器能力十分有限&#xff0c;Web前端开发者希望能够通过一定的机制来扩展浏览器的能力。早期的方法就是插件机制&#xff0c;现在流行次啊用混合编程&#xff08;Hybird Programming&#xff09;模式。插件…