【算法】—— 简单多状态 dp 问题

news2024/11/19 20:37:02

在上期,我给大家讲解了关于单个状态下的dp问题,本期我给大家讲述几道关于多状态下的dp问题。希望大家有所帮助!!!

目录

(一)粉刷房⼦

(二)买卖股票的最佳时机含冷冻期

(三)买卖股票的最佳时机含手续费


(一)粉刷房⼦

  • 链接如下:剑指 Offer II 091. 粉刷房子

题⽬描述:

算法思路: 

1. 状态表⽰:

对于线性 dp ,我们可以⽤「经验 + 题⽬要求」来定义状态表⽰:

  • i. 以某个位置为结尾;
  • ii. 以某个位置为起点。

这⾥我们选择⽐较常⽤的⽅式,以某个位置为结尾,结合题⽬要求,定义⼀个状态表⽰:

但是我们这个题在 i 位置的时候,会⾯临「红」「蓝」「绿」三种抉择,所依赖的状态需要细分:

  • dp[i][0] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「红⾊」,此时的最⼩花 费;
  • dp[i][1] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「蓝⾊」,此时的最⼩花 费;
  • 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. 初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。

使⽤这种技巧要注意两个点:

  • i. 辅助结点⾥⾯的值要「保证后续填表是正确的」;
  • ii. 「下标的映射关系」。 在本题中,添加⼀个节点,并且初始化为 0 即可。

4. 填表顺序

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

5. 返回值

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


代码展示:

class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        int size = costs.size();
        vector<vector<int>> dp(size + 1, vector<int>(3));
        for (int i = 1; i <= size; 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][1], dp[i - 1][0]) + costs[i - 1][2];
        }
    return min(dp[size][0], min(dp[size][1], dp[size][2]));
    }
};
  • Leetcode 测试结果:

 性能分析:

  • 时间复杂度:O(n)。其中 n 是房子个数。需要遍历全部房子一次,由于颜色数量固定是三种,因此对于每个房子计算粉刷房子的最小花费成本的时间是 O(1),总时间复杂度是 O(n)。
     
  • 空间复杂度:O(n).

(二)买卖股票的最佳时机含冷冻期

  • 链接如下:309. 最佳买卖股票时机含冷冻期

在之前我已经对买卖股票的最佳时机等进行了具体的探讨,如果大家对于不清楚的话,可以去看我关于买卖股票最佳时机的题目讲解:买卖股票最佳时机类题目讲解

题目描述

 算法思路:

1. 状态表⽰:

对于线性 dp ,我们可以⽤「经验 + 题⽬要求」来定义状态表⽰:

  • i. 以某个位置为结尾;
  • ii. 以某个位置为起点。

这⾥我们选择⽐较常⽤的⽅式,以某个位置为结尾,结合题⽬要求,定义⼀个状态表⽰:

由于有「买⼊」「可交易」「冷冻期」三个状态,因此我们可以选择⽤三个数组,其中:

  • dp[i][0] 表⽰:第 i 天结束后,处于「买⼊」状态,此时的最⼤利润;
  • dp[i][1] 表⽰:第 i 天结束后,处于「可交易」状态,此时的最⼤利润;
  • dp[i][2] 表⽰:第 i 天结束后,处于「冷冻期」状态,此时的最⼤利润。

2. 状态转移⽅程:

我们要谨记规则:

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

ii. 处于「卖出」状态的时候:

  •  如果「在冷冻期」,不能买⼊;
  •  如果「不在冷冻期」,才能买⼊。

对于 dp[i][0] ,我们有「两种情况」能到达这个状态:

  • i. 在 i - 1 天持有股票,此时最⼤收益应该和 i - 1 天的保持⼀致: dp[i - 1] [0] ;
  • ii. 在 i 天买⼊股票,那我们应该选择 i - 1 天不在冷冻期的时候买⼊,由于买⼊需要花 钱,所以此时最⼤收益为: dp[i - 1][1] - prices[i]

两种情况应取最⼤值,因此: dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]) 。

对于 dp[i][1] ,我们有「两种情况」能到达这个状态:

  • i. 在 i - 1 天的时候,已经处于冷冻期,然后啥也不⼲到第 i 天,此时对应的状态为: dp[i - 1][2] ;
  • ii. 在 i - 1 天的时候,⼿上没有股票,也不在冷冻期,但是依旧啥也不⼲到第 i 天,此时 对应的状态为 dp[i - 1][1] ;

两种情况应取最⼤值,因此: dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]) 。

对于 dp[1][i] ,我们只有「⼀种情况」能到达这个状态:

  • i. 在 i - 1 天的时候,卖出股票。 因此对应的状态转移为: dp[i][2] = dp[i - 1][0] + prices[i] 

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(vector<int>& prices) {
        int size = prices.size();
        vector<vector<int>> dp(size, vector<int>(3));
        dp[0][0] = -prices[0];
        for(int i = 1; i < size; 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][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }
        return max(dp[size - 1][1], dp[size - 1][2]);
    }
};
  • Leetcode 测试结果:

 性能分析:

  • 时间复杂度:O(n)。
     
  • 空间复杂度:O(n).

(三)买卖股票的最佳时机含手续费

  • 链接如下:714. 买卖股票的最佳时机含手续费

题目描述

算法思路 

1. 状态表⽰:

对于线性 dp ,我们可以⽤「经验 + 题⽬要求」来定义状态表⽰:

  • i. 以某个位置为结尾,巴拉巴拉;
  • ii. 以某个位置为起点,巴拉巴拉。

这⾥我们选择⽐较常⽤的⽅式,以某个位置为结尾,结合题⽬要求,定义⼀个状态表⽰:

由于有「买⼊」「可交易」两个状态,因此我们可以选择⽤两个数组,其中:

  •  f[i] 表⽰:第 i 天结束后,处于「买⼊」状态,此时的最⼤利润;
  • g[i] 表⽰:第 i 天结束后,处于「卖出」状态,此时的最⼤利润。

2. 状态转移⽅程:

我们选择在「卖出」的时候,⽀付这个⼿续费,那么在「买⼊」的时候,就不⽤再考虑⼿续费的问 题。

对于 f[i] ,我们有两种情况能到达这个状态:

  • i. 在 i - 1 天「持有」股票,第 i 天啥也不⼲。此时最⼤收益为 f[i - 1] ;
  • ii. 在 i - 1 天的时候「没有」股票,在第 i 天买⼊股票。此时最⼤收益为 g[i - 1] - prices[i]) ;

两种情况下应该取最⼤值,因此  f[i] = max(f[i - 1], g[i - 1] - prices[i]) 

对于 g[i] ,我们也有两种情况能够到达这个状态:

  • i. 在 i - 1 天「持有」股票,但是在第 i 天将股票卖出。此时最⼤收益为: f[i - 1] + prices[i] - fee) ,记得⼿续费;
  • ii. 在 i - 1 天「没有」股票,然后第 i 天啥也不⼲。此时最⼤收益为: g[i - 1] ; 两种情况下应该取最⼤值,因此 g[i] = max(g[i - 1], f[i - 1] + prices[i] - fee) 。

3. 初始化:

由于需要⽤到前⾯的状态,因此需要初始化第⼀个位置。

  • 对于 f[0] ,此时处于「买⼊」状态,因此 f[0] = -prices[0] ;
  • 对于 g[0] ,此时处于「没有股票」状态,啥也不⼲即可获得最⼤收益,因此 g[0] = 0 。

4. 填表顺序:

  • 毫⽆疑问是「从左往右」,但是两个表需要⼀起填。

5. 返回值:

  • 应该返回「卖出」状态下,最后⼀天的最⼤值收益: g[n - 1] 。

代码展示

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int size = prices.size();
        vector<int> f(size);
        auto g = f;
        f[0] = -prices[0];
        for(int i = 1; i < size; i++){
            f[i] = max(f[i - 1], g[i - 1] - prices[i]);
            g[i] = max(g[i - 1], f[i - 1] + prices[i] - fee);
        }
        
        return g[size - 1];
    }
};
  • Leetcode 测试结果:

性能分析:

  • 时间复杂度:O(n)。
     
  • 空间复杂度:O(1).

 


以上便是关于简单多状态 dp 的三道题目的讲解。希望对大家有所帮助,感谢大家的阅读!!!

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

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

相关文章

破解提升 LLMs 性能的黑匣子—— LlamaIndex

“可以将 LlamaIndex 视为外部数据和 LLM 连接在一起的黑匣子。”在 Zilliz 组织的网络研讨会中&#xff0c;LlamaIndex 的联合创始人兼首席执行官 Jerry Liu 曾这样说道。 对于 Jerry Liu 的这个比喻&#xff0c;熟悉 LLMs 的开发者会觉得颇为贴切&#xff0c;尤其是对于那些想…

Chinese-LLaMA-Alpaca代码实战

文章目录 微调chinese-alpaca部署llama.cpp将FP16模型量化为4-bit 项目地址&#xff1a; https://github.com/ymcui/Chinese-LLaMA-Alpaca 微调chinese-alpaca 本项目基于中文数据 开源了使用中文文本数据预训练的中文LLaMA大模型&#xff08;7B、13B&#xff09;开源了进一…

JavaFX【TableView使用详解】

目录 概述 组件 Student ObservableList TableView setCellValueFactory() TableColumn 1. Callback 2. PropertyValueFactory 增加到末行 1、tableView.getItems().add(Student s) 2、list.add(Student s) 删除指定行 1、tableView.getItems().remove(int i) 2、…

软件测试完后,运行后还有BUG,测试人员就应该背锅吗?

测试完成后还有bug&#xff0c;测试人员肯定是有责任的&#xff0c;第一时间要赶紧处理而不是着急甩锅。但是这口锅全部扣测试身上&#xff0c;明显也是不能接受的&#xff0c;关键在于测试人员需要找出足够的证据来保护自己。 或许很多人会说测试不可能发现所有的bug&#xf…

软件测试人的第一个实战项目:web端(视频教程+文档+用例库)

最近很多自学测试的小伙伴在问我&#xff0c;学完基础的知识后面临着项目问题&#xff0c;网上项目大都不全&#xff0c;而且也不知道该怎么做&#xff0c;不知道做哪些项目能帮助自己提升 今天给大家分享一个web软件测试实战项目&#xff0c;该项目对新手十分友好&#xff0c;…

STM32入门100步(第4步~第5步)

第4~5步 STM32内部核心功能 前两节中,我们了解了什么是ARM、什么是STM32系列,接下来就是了解具体的一款STM32单片机的内部功能。单片机就是一种微小型计算机,其核心原理就是计算机原理。对于其他非ARM构架的单片机来说学习过程也是相似的。 我们学习的是STM32F103C8T6这款单…

细说前端打包发布后,浏览器缓存如何清理?其实只需要简单的webpack配置就行

前言 有没有这么一种场景&#xff0c;项目上线后&#xff0c;客户使用过程中发现了bug&#xff0c;你急急忙忙改完&#xff0c;发布。但你发布后 测试人员或者客户会说&#xff1a;“你这改了没用啊”。 你&#xff1a;“清下缓存试试” 客户&#xff1a;“&#xff1f;&am…

c#——WCF和HTTP文件传输实验

&#xff08;1&#xff09;掌握HTTP协议下WCF服务应用程序构建方法。 &#xff08;2&#xff09;掌握WCF客户端和服务端的消息交换模式。 &#xff08;3&#xff09;掌握协定的设计及实现方法。 &#xff08;4&#xff09;熟悉WCF和HTTP的相关绑定设置。 &#xff08;5&#xf…

CentOS7编译安装Python3.10(含OpenSSL1.1.1安装),创建虚拟环境,运行Django项目(含sqlite版本报错)

文章目录 1、CentOS安装OpenSSL1.1.1&#xff08;前置环境&#xff09;2、CentOS安装 Python 3.103、创建虚拟环境4、运行Django项目 1、CentOS安装OpenSSL1.1.1&#xff08;前置环境&#xff09; 编译安装Python3.10时需要openssl1.1.1 查看当前版本 & 删除openssl1.0 …

Java高并发核心编程—JUC显示锁原理

注&#xff1a;本笔记是阅读《Java高并发核心编程卷2》整理的笔记&#xff01; 显示锁 使用Java内置锁时&#xff0c;不需要通过Java代码显式地对同步对象的监视器进行抢占和释放&#xff0c;这些工作由JVM底层完成&#xff0c;而且任何一个Java对象都能作为一个内置锁使用&a…

Apache Kafka - ConsumerInterceptor 实战 (1)

文章目录 概述使用场景实战配置文件配置类自定义ConSumerInterceptor使用 概述 ConsumerInterceptor是Kafka中的一个重要组件&#xff0c;它允许开发人员在Kafka消费者端拦截和修改消息的处理过程。ConsumerInterceptor可以用于实现各种功能&#xff0c;从消息监控到数据转换和…

【Python开发】FastAPI 01:hello world

FastAPI 是一个轻量级的后端框架&#xff0c;对于定制化不高或者功能简单的业务完全可以拿他作为后台。 FastAPI 一个比较重要的特性就是异步&#xff0c;简单来说就是相比 django 和 flask 快&#xff0c;FastAPI 和 flask 的语法类似。本篇文章介绍 FastAPI 运用的简单实例&a…

从索引结点出发探索软、硬链接

索引结点的初步认识 对于整个计算机系统的资源管理&#xff0c;我们可以认为&#xff0c;OS先将这些资源的数据信息&#xff0c;给描述起来构成一个部分&#xff0c;然后再将它们组织起来&#xff0c;就能够实现由OS集中管理。举一个最经典的例子&#xff0c;进程的引入是为了…

Cos上传(腾讯云):图片存储方案

Cos上传(腾讯云) 01.图片存储方案介绍 目标 了解主流的图片存储方案 两种常见方案 方案一&#xff1a;存到自己公司购买的服务器上 优点&#xff1a;好控制 缺点&#xff1a;成本高由于图片都存放到自己的服务器上&#xff0c;占据空间很大 方案二&#xff1a;存到三方…

总结SpringBoot常用读取配置文件的3种方法

文章目录 1、使用 Value 读取配置文件2、 使用 ConfigurationProperties 读取配置文件3、读取配置文件中的List 1、使用 Value 读取配置文件 注&#xff1a;这种方法适用于对象的参数比较少的情况 使用方法&#xff1a; 在类上添加configuration注解&#xff0c;将这个配置对…

Hotbit交易平台停运,百万用户待清退,币圈危机再度蔓延

“币圈”的危机似乎还没有走到尽头。5月22日&#xff0c;加密货币交易平台Hotbit发文宣布&#xff0c;决定从世界标准时间当日4:00停止所有CEX&#xff08;中心化交易所&#xff09;操作&#xff0c;希望所有用户在6月21日4:00之前提取剩余资产。据悉&#xff0c;该平台在其任期…

微前端乾坤

1. 乾坤 简介 qiankun 是一个基于 single-spa 的微前端实现库&#xff0c;旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统 官网&#xff1a;https://qiankun.umijs.org/zh/guide 2.使用 背景&#xff1a; vue2.0 , vue-cli 5.0 主应用&#xff1a; 安装乾坤…

基于上下文折扣的多模态医学图像分割证据融合

文章目录 Evidence Fusion with Contextual Discounting for Multi-modality Medical Image Segmentation摘要本文方法Evidential SegmentationMulti-modality Evidence FusionDiscounted Dice Loss 实验结果 Evidence Fusion with Contextual Discounting for Multi-modality …

利用PaddleOCR识别增值税发票平台验证码(开箱即用)

前言:全国增值税发票查验平台验证码没什么好说的,根据指定的颜色识别验证码中的文字,图片如下 下面直接讲解利用paddleocr识别的思路,为什么使用paddleocr,因为paddle中集成了较好的ocr文字识别模型,开箱即用即可,废话不多说,剑指主题,识别思路步骤如下 步骤如下 1、…

BI技巧丨度量值的动态格式字符串

2023年4月版本新增了度量值的动态格式字符串功能。从字面上看可能小伙伴们不是很理解这个功能的用途&#xff0c;这里白茶给大家解释一下。 通俗一点来说&#xff0c;就是可以在数值中加入文本&#xff0c;将其转化为字符串&#xff0c;而不改变其原有的数据类型。 看到这里&…