第四十六章 动态规划——状态机模型

news2024/11/27 14:26:34

第四十六章 动态规划——状态机模型

  • 一、通俗理解状态机DP
    • 1、什么是状态机
    • 2、什么是状态机DP
  • 二、例题
    • 1、AcWing 1049. 大盗阿福
      • (1)问题
      • (2)分析
        • a.状态定义
        • b.状态转移
        • c.循环设计
        • d.初末状态
      • (3)代码
    • 2、AcWing 1057. 股票买卖 IV
      • (1)问题
      • (2)分析
        • a.状态定义
        • b.状态转移
        • c.循环设计
        • d.初末状态
      • (3)代码
        • 规定卖出算一次交易
        • 规定买入算一次交易

一、通俗理解状态机DP

其实状态机DP只是听起来高级,其实我们之前做的所有关于DP的题几乎都算是状态机,为什么呢?

1、什么是状态机

大家继续向下看:

DP解决的是多决策的问题,那么我们可以把01背包问题画成下面的图:
在这里插入图片描述按照正常的逻辑,我们一般都是从第一个物品开始看,决定选或者不选,然后再去看第二个物品。那么我们现在只看其中一个物品,这个物品无非就两个状态,在背包里,不在背包里。

其实这两个状态就构成了一个状态机。
在这里插入图片描述

写到这里,我们就可以通俗地理解一下状态机,状态机的作用就是记录一个事件所有可能的存在状态以及这些状态之间的联系。这个作用或许大家现在还会感到一些模糊,但是没关系,后面的例题中作者会进行详细地讲解,让大家深刻体会到。

2、什么是状态机DP

那么什么是状态机DP呢?这种DP方式和之前的DP有什么不同呢?

状态机DP就是在DP数组中专门开辟一个维度记录当前事件所处的状态,比如背包问题如果写成状态机DP的模式,我们会写成 f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]或者 f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0],此时的1代表第i个物品在背包里,0说明第i个物品不在背包里。状态定义可以写作:在前i个物品中选,背包容量为j,且第i个物品在(或者不在)背包中的时候,我们所能携带的最大价值。

其实通过这个例子,大家就能明显地感觉到,在状态机模型中我们强调并记录当前事物的状态

可是这样做有啥用呢?

作者可以给01背包问题加上一个情景,不能选择相邻的物品。此时如果还按照我们之前的做法,用f[i][j]来定义的话,你对某个物品的具体状态是模糊的。我们一般是用f[i-1][j]和f[i-1][j-v]+w来写转移方程,但是你无法判断第i-1个物品是否在背包里,也就是说你不知道第i-1个物品所处的状态。

但是此时如果按照状态机模型来写,特意强调一下某个物品的状态,我们就可以利用第三维度的0和1来判断能否选当前的物品。

那么此时可以总结一下什么时候用状态机DP,当某个事件的状态影响到后面事件的决策的时候,我们需要在状态定义的时候记录事件状态,即状态机DP。

二、例题

1、AcWing 1049. 大盗阿福

(1)问题

在这里插入图片描述

(2)分析

这道题其实就算是一个01背包问题加状态机DP,如果转化为01背包问题的话,即:从1到i个物品里面选,不限制背包容量但是不能够选择相邻物品的情况下,所能携带的最大价值。

a.状态定义

按照刚刚的状态机的解释来看,这道题中把某个店铺看作一个事件,那么抢或者不抢就是这个店铺的状态。而状态机往往也记录了一个事件中各个状态的转移。

但是这里为了解释的更加清楚,我们找到两个相邻的店铺,分别表示出他们的状态,画出状态之间的转移:如下图所示
在这里插入图片描述
由于题目中强调不能抢相邻的,所以如果我们抢了店铺A,那么下一次就不能抢店铺B,(假设A和B相邻)。

所以我们的f[i][j]表示的是,在前i个店铺里抢劫,且抢(或者不抢)店铺i的情况下,所能抢得的最大价值。0表示不抢,1表示抢。

b.状态转移

上面这个图其实也说明了状态转移方程的书写思路。这里就直接写了。
f ( i , 1 ) = f ( i − 1 , 0 ) + w [ i ] f ( i , 0 ) = m a x ( f ( i − 1 , 1 ) , f ( i − 1 , 0 ) ) f(i,1)=f(i-1,0)+w[i]\\ f(i,0)=max(f(i-1,1),f(i-1,0)) f(i,1)=f(i1,0)+w[i]f(i,0)=max(f(i1,1),f(i1,0))
最后在f(n,1)和f(n,0)中选一个最大值。

c.循环设计

根据转移方程,我们写一个一维的循环即可。

d.初末状态

根据转移方程,我们需要初始化的是两个状态:f[0][0]和f[0][1]。很明显第0个店铺是不存在的,所以f[0][1]是不合法的,初始化为负无穷即可。或者从另一个角度理解,我们的第1个店铺可选可不选,所以第0个店铺一定是不能选的,那么为了不选第0个,只能将f[0][1]变成负无穷。
其实这里不管它也能过,因为店铺的价值最少就是0,虽然这个状态不合法, 但是不影响答案。可是在后续的题目中,初始化会对答案造成一定影响,所以最好还是对不合法的状态进行一定的初始化。

(3)代码

#include<iostream>
using namespace std;
const int N=1e5+10,INF=0x3f3f3f3f;
int w[N],f[N][2];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)scanf("%d",w+i);
        f[0][0]=0,f[0][1]=-INF;
        for(int i=1;i<=n;i++)
        {
            f[i][0]=max(f[i-1][0],f[i-1][1]);
            f[i][1]=f[i-1][0]+w[i];   
        }
        cout<<max(f[n][0],f[n][1])<<endl;
    }

    return 0;   
}

2、AcWing 1057. 股票买卖 IV

(1)问题

在这里插入图片描述

(2)分析

这道题我们首先得明确一点,我们只有一支股票,只是这支股票在不同天有着不同的价格,因此我们可以把天作为单位划分不同的状态。同时这道题中还有一个关键的信息,同一时刻,我们只能做一次交易,什么意思呢?即当这只股票在我们的手里的时候,我们无法再次进行购买,只能把这个股票卖出或者不卖。如果这支股票不在我们手里,那么我们只能选择买或者不买。

同时,题目还规定,买入股票和卖出股票算一次交易。因此,我们可以认为二者各算半次交易。由于只有手中有股票的时候才能卖出,说明再次之前已经买入。因此,我们可以在卖出股票的时候,把我们的交易次数+1。

a.状态定义

f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]表示在1到i天中买卖股票,当前已经进行了j次交易,且在第i天里,手中没有当前股票的时候,所得到的最大利润。
f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]表示在1到i天中买卖股票,当前已经进行了j次交易,且在第i天里,手中有当前股票的时候,所得到的最大利润。

b.状态转移

状态转移的话,我们可以画出下面的图,这样更加清晰:
在这里插入图片描述

c.循环设计

这道股票的题其实如果不看状态之间的影响的话,其实就是一个01背包,因此我们按照01背包的逻辑来循环即可,即外层循环i,内层循环j。

d.初末状态

这里初始化比较麻烦,因为我们将卖出股票算作一次完整的交易,这一规定使得我们的初始化变得比较复杂。根据我们的规定,f[i][0][1]这种定义也是存在的,即从0到i中选一个最便宜的购入,就是当下的最优解。但是f[0][0][1]是不合法的,因此第0天的股票多贵不知道,没法买,而f[0][0][0]初始化为0即可,这个状态是存在的,第0天的股票虽然不存在,但是我们不买,依旧是0。

如果不想这么复杂的话,我们可以规定买入股票的时候,算一次完整的交易。此时我们发现f[i][0][1]是不合法的了。因为既然有股票,交易次数就不是0了。

(3)代码

规定卖出算一次交易

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10,k=110;
int f[N][k][2],w[N];
int n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%d",w+i);
    memset(f,0xcf,sizeof f);
    f[0][0][0]=0;
    for(int i=1,minv = 1e6;i<=n;i++)
    {
        f[i][0][0]=0;
        minv=min(minv,w[i]);
        f[i][0][1]=-minv;
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][1]+w[i]);
            f[i][j][1]=max(f[i-1][j][1],f[i-1][j][0]-w[i]);
        }
    }
    int res=0;
    for(int i=0;i<=m;i++)res=max(f[n][i][0],res); 
    cout<<res<<endl;
    return 0;
}

规定买入算一次交易

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10,k=110;
int f[N][k][2],w[N];
int n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%d",w+i);
    memset(f,0xcf,sizeof f);
    for(int i=0;i<=n;i++)f[i][0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+w[i]);
            f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0]-w[i]);
        }
    }
    int res=0;
    for(int i=0;i<=m;i++)res=max(f[n][i][0],res); 
    cout<<res<<endl;
    return 0;
}

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

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

相关文章

C++学习/温习:新型源码学编程(三)

写在前面(祝各位新春大吉&#xff01;兔年如意&#xff01;) 【本文持续更新中】面向初学者撰写专栏&#xff0c;个人原创的学习C/C笔记&#xff08;干货&#xff09;所作源代码输出内容为中文&#xff0c;便于理解如有错误之处请各位读者指正请读者评论回复、参与投票&#xf…

01 课程简介、HTML标签【尚硅谷JavaWeb教程】

1. 课程体系设计 2. HTML标签 服务器—浏览器&#xff08;字符串"" &#xff09; demo01.html 1&#xff09;html语言是解释型语言&#xff0c;不是编译型 浏览器是容错的 2&#xff09;html页面中由一对标签组成&#xff1a; < html>称为 开始标签 < /htm…

Java基础语法——数组概念、数组内存图解(一个数组、二个数组)及二元数组的应用

目录 数组概述 数组定义格式 数组概念 数组的定义格式 数组的初始化 数组初始化概述 数组的初始化方式 Java中的内存分配 Java中一个数组的内存图解 Java中二个数组的内存图解 两个数组指向同一个地址的内存图解 数组操作中两个常见的小问题 二维数组 二维数组概述…

c++11 标准模板(STL)(std::forward_list)(十一)

定义于头文件 <forward_list> template< class T, class Allocator std::allocator<T> > class forward_list;(1)(C11 起)namespace pmr { template <class T> using forward_list std::forward_list<T, std::pmr::polymorphic_…

前端架构处理Cookie、Session、Token

1. Cookie Cookie 总是保存在客户端中。按在客户端中的存储位置&#xff0c;可分为内存 Cookie 和硬盘 Cookie。 内存 Cookie 由浏览器维护&#xff0c;保存在内存中&#xff0c;浏览器关闭后就消失了&#xff0c;其存在时间是短暂的。硬盘 Cookie 保存在硬盘里&#xff0c;…

Spring Boot、Spring MVC热部署

一、相关概述 JVM能够识别的是字节码.class文件每次重新运行都是一个重新编译的过程&#xff0c;也就是说会生成新的target字节码文件&#xff1b;但是每次修改了代码之后也必须要重新运行&#xff0c;这样比较麻烦。热部署就能较好地解决该问题&#xff0c;直接刷新页面就可以…

(22)go-micro微服务kibana使用

文章目录一 kibana介绍二 Kibana主要功能三 Kibana侧边栏四 Kibana安装1.拉取镜像2.运行命令3.查看是否运行五 Kibana使用六 Kibana图形化界面七 最后一 kibana介绍 Kibana &#xff1a;是一个开源的分析和可视化平台&#xff0c;旨在与 Elasticsearch 合作。Kibana 提供搜索、…

初识Nonebot2

文章目录什么是nonebot2&#xff1f;机器人工作流程WebSocket协议OneBot标准Nonebot2框架工作路径什么是nonebot2&#xff1f; 关于NoneBot2&#xff0c;我们先来引用一下官方文档的描述 NoneBot2是一个可扩展的 Python 异步机器人框架&#xff0c;它会对机器人收到的事件进行…

创建进度条 tqdm模块

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 创建进度条 tqdm模块 选择题 以下python代码最后输出错误的一项是? from tqdm import tqdm import time text{loss:0.2,learn:0.8} print(【开始执行】) for i in tqdm(range(50)…

python图像处理(图像缩放)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 图像缩放也是isp处理的一个基本功能。现在的camera像素越来越大,但是显示设备的分辨率是一定的,如果想把图像显示在显示器或者lcd上面,那就要符合对应显示设备的分辨率。一般来说…

【数据结构与算法理论知识点】5、图与贪心算法

5、图与贪心算法 逻辑结构 5.1、图的定义和术语 图&#xff1a;Graph(V,E) V:顶点&#xff08;数据元素&#xff09;的有穷非空集合&#xff1b; E:边的有穷集合。 **无向图:**每条边都是无方向的 有向图:每条边都是有方向的 完全图:任意两个点都有一条边相连 稀疏图:有很…

SSM 农产品销售管理系统

SSM 农产品销售管理系统 SSM 农产品销售管理系统 功能介绍 首页 用户登录注册 图片轮播 最新上架 农产资讯 产品商城 产品详情 在线留言 加入购物车 提交购买 评论 收藏 系统简介 系统公告 关于我们 友情链接 后台管理 登录 系统用户管理 修改密码 用户权限管理 产品信息管理…

Hadoop基础之《(4)—Yarn概述》

一、什么是Yarn YARN&#xff08;Yet Another Resource Negotiator&#xff09;&#xff0c;简称YARN&#xff0c;另一种资源协调者&#xff0c;是Hadoop的资源管理器。 二、Yarn的组成 Yarn有两个核心组件&#xff0c;ResourceManager和NodeManager。 1、ResourceManager&am…

UE Operation File [ Read / Write ] DTOperateFile 插件说明

蓝图直接操作文件的功能节点 可以获取文件基本信息以及读写文件 目录 1. File Exists 2. File Size 3. File Delete 4. File Is Read Only 5. File Move 6. File Save By String 7. File Save By Array String 8. File Save By Array Binary 9. File Append By Strin…

Jenkins(一):什么是Jenkins?

目录 一、Jenkins是什么&#xff1f; 二、安装步骤 1.访问官网&#xff0c;下载安装包 2.解锁Jenkins 3.设置Jenkins的工作目录&#xff08;可选&#xff09; 总结 前言 近期在研究自动化部署&#xff0c;在这里对Jenkins做个笔记。 一、Jenkins是什么&#xff1f; Jenk…

数据挖掘,计算机网络、操作系统刷题笔记36

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记36 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…

泊松过程与排队论

全国同理:随机过程随机会过 ......唉 你说热统,他为什么要搭配着随机过程一起学 ......唉 你说数模比赛 为什么不能白送一个奖牌小摘要泊松过程泊松过程来到间隔与等待时间的分布来到时刻的条件分布M/G/1 忙期非齐次泊松过程复合泊松过程条件泊松过程排队系统输入常见的输入分布…

Python eval()和exec()函数详解

eval() 和 exec() 函数都属于 Python 的内置函数&#xff0c;由于这两个函数在功能和用法方面都有相似之处&#xff0c;所以将它们放到一节进行介绍。eval() 和 exec() 函数的功能是相似的&#xff0c;都可以执行一个字符串形式的 Python 代码&#xff08;代码以字符串的形式提…

Elasticsearch:深入理解 Elasticsearch 查询:过滤器查询 vs 全文搜索

如果我必须用一句话来描述 Elasticsearch&#xff0c;我会这样说&#xff1a; 当搜索遇到大规模分析时&#xff08;近乎实时&#xff09; Elasticsearch 是目前最受欢迎的 10 大开源技术之一。 公平地说&#xff0c;它包含许多本身并不独特的关键功能&#xff0c;但是&#xff…

ARP渗透与攻防(八)之ARP攻击防御

系列文章 ARP渗透与攻防(一)之ARP原理 ARP渗透与攻防(二)之断网攻击 ARP渗透与攻防(三)之流量分析 ARP渗透与攻防(四)之WireShark截获用户数据 ARP渗透与攻防(五)之Ettercap劫持用户流量 ARP渗透与攻防(六)之限制网速攻击 ARP渗透与攻防(七)之Ettercap Dns劫持 ARP攻击防御 …