数据结构与算法学习笔记(Acwing 提高课)----动态规划·状态机模型

news2025/5/25 1:14:48

数据结构与算法学习笔记----动态规划·状态机模型

@@ author: 明月清了个风
@@ first publish time: 2025.5.20

ps⭐️背包终于结束了,状态机模型题目不多。状态机其实是一种另类的状态表示方法,将某一个点扩展为一个状态进行保存并在多个状态之间转移,具体的来看题目理解吧。

Acwing 1049. 大盗阿福

阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。
这条街上一共有$N $家店铺,每家店中都有一些现金。阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。
作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?

输入格式

输入的第一行是一个整数 T T T,表示一共有 T T T组数据。
接下来的每组数据,第一行是一个整数 N N N ,表示一共有N家店铺。第二行是 N N N个被空格分开的正整数,表示每一家店铺中的现金数量。每家店铺中的现金数量均不超过1000。

输出格式

对于每组数据,输出一行。该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。

数据范围

1 ≤ T ≤ 50 1 \le T \le 50 1T50,

1 ≤ N ≤ 100000 1 \le N \le 100000 1N100000

思路

很明显,这道题是一道线性的问题,题目并没有对抢劫的顺序有特殊的规定,因此可以从前往后。

对于状态表示而言,使用 f [ i ] f[i] f[i]表示抢劫前 i i i家店的最大收益。

然后就是状态划分,同样根据最后一步的选择进行,若不抢劫第 i i i家店铺,那就是 f [ i − 1 ] f[i - 1] f[i1],若抢劫第 i i i家店铺,那么意味着第 i − 1 i - 1 i1家店铺就无法选择了,因此相当于只能从前 i − 2 i - 2 i2家店铺进行选择的最大值 f [ i − 2 ] + w [ i ] f[i - 2] + w[i] f[i2]+w[i]

但是上述分析其实在更新一个状态时,用到了前面两次的状态,下面考虑如何进行优化。

在上面的分析中,当仅用上一轮的状态也就是不知道第 i − 2 i - 2 i2的状态,如果我们抢劫第 i i i家店铺的时候,无法知道在最优解中第 i − 1 i - 1 i1家店铺是否被抢劫了,无法进行转移,因此引入状态机的表示方法,将所有状态表示为 f [ i ] [ 0 ] f[i][0] f[i][0] f [ i ] [ 1 ] f[i][1] f[i][1] 0 0 0表示当前店铺未选择, 1 1 1表示当前店铺被选择了,这样就将每个店铺的两个状态分开了,对于 f [ i ] [ 0 ] f[i][0] f[i][0],表示第 i i i个店铺没有被选择,因此可以从 f [ i − 1 ] [ 1 ] f[i - 1][1] f[i1][1] f [ i − 1 ] [ 0 ] f[i - 1][0] f[i1][0]转移过来;对于 f [ i ] [ 1 ] f[i][1] f[i][1],表示第 i i i个店铺被选择了,因此只能从 f [ i − 1 ] [ 0 ] f[i - 1][0] f[i1][0]转移过来。

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 10010, inf = 0x3f3f3f3f;

int T;
int n;
int f[N][2];
int w[N];


int main()
{
    cin >> T;
    
    while(T --)
    {
        cin >> n;
        
        for(int i = 1; i <= n; i ++) cin >> w[i];
        
        f[0][0] = 0, f[0][1] = -0x3f3f3f3f;
        
        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][1], f[n][0]) << endl;
    
    }
    
    return 0;
}

Acwing 1057. 股票买卖 IV

给定一个长度为 N N N的数组,数组中的第 i i i个数字表示一个给定股票在第 i i i天的价格。

设计一个算法来计算你所能获取的最大利润,你最多可以完成 k k k笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。一次买入卖出合为一笔交易。

输入格式

第一行包含整数 N N N k k k,表示数组的长度以及你可以完成的最大交易笔数。

第二行包含 N N N个不超过 10000 10000 10000的非负整数,表示完整的数组。

输出格式

输出一个整数,表示最大利润。

数据范围

1 ≤ N ≤ 10 5 1 \le N \le 10^5 1N105,

1 ≤ k ≤ 100 1 \le k \le 100 1k100

思路

这道题很明显也可以看成两个状态,一个是手中有股票(已经买入了一个股票),另一个是手中没有股票(可以买入一个股票),那么状态的转移是:手中有股票时可以卖出不可以再次买入,也可以什么都不做;手中无股票时可以买入不可以卖出,也可以什么都不做。

当在某一天要卖出股票时,相当于获得这一天的权重 w [ i ] w[i] w[i];当要在某一天买入时,就要减去当天的权重 w [ i ] w[i] w[i]

搞清楚题目的意思后,可以看状态表示了,使用 f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0] f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]表示前 i i i天完成了 j j j笔交易且当前的状态为 0 0 0 1 1 1 0 0 0表示手中没有股票, 1 1 1表示手中有股票,也就是正在进行第 j j j次交易,属性就是集合的最大值。

那么对于状态转移, f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]可以从 f [ i − 1 ] [ j ] [ 0 ] f[i - 1][j][0] f[i1][j][0]走过来,也可以从 f [ i − 1 ] [ j ] [ 1 ] f[i - 1][j][1] f[i1][j][1]转移过来,因此 f [ i ] [ j ] [ 0 ] = m a x ( f [ i − 1 ] [ j ] [ 0 ] , f [ i − 1 ] [ j ] [ 1 ] + w [ i ] ) f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1] + w[i]) f[i][j][0]=max(f[i1][j][0],f[i1][j][1]+w[i]);对于 f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]来说,其状态转移方程为 f [ i ] [ j ] [ 1 ] = m a x ( f [ i − 1 ] [ j ] [ 1 ] , f [ i − 1 [ j − 1 ] [ 0 ] ] − w [ i ] ) f[i][j][1] = max(f[i - 1][j][1], f[i - 1[j - 1][0]] - w[i]) f[i][j][1]=max(f[i1][j][1],f[i1[j1][0]]w[i]).

需要注意的是初始化状态,当交易次数为 0 0 0的时候,手中不可能持有股票,因此都为非法状态,需要进行标记,而交易次数为 0 0 0时,手中没有股票为合法状态,初始化为 0 0 0即可。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010, M = 110;

int n, m;
int w[N];
int f[N][M][2];

int main()
{
    cin >>  n >> m;
    
    for(int i = 1; i <= n; i ++) cin >> w[i];
    
    memset(f, -0x3f, 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 = 1; i <= m; i ++) res = max(res, f[n][i][0]);
    
    cout << res << endl;
    
    return 0;
}

Acwing 1058. 股票买卖V

给定一个长度为 N N N的数组,数组中的第 i i i个数字表示一个给定股票在第 i i i天的价格。

设计一个算法来计算你所能获取的最大利润,在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一只股票)

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票(即冷却期为 1 1 1天)。

输入格式

第一行包含整数 N N N,表示数组长度。

第二行包含 N N N个不超过 10000 10000 10000的正整数,表示完整的数组。

输出格式

输出一个整数,表示最大利润。

数据范围

1 ≤ N ≤ 10 5 1 \le N \le 10^5 1N105,

思路

在这一题中添加了一个条件,在交易过后会有一天的冷冻期,因此变成了三个状态:手中有股票,手中没有股票的第一天,手中没有股票的第 ≥ 2 \ge 2 2天。相当于把上一题中的第二个状态再次进行了分割。

这三个状态的转换关系如下:

手中有货可由手中有货转移而来,他可转移至手中无货的第一天(即第二种状态),手中无货的第一天只能向第三种状态进行转移;第三种状态可以转移到自身,也可转移到手中有货(即第一种状态)。

同样地,可以使用 f [ i ] [ 0 ] , f [ i ] [ 1 ] , f [ i ] [ 2 ] f[i][0],f[i][1],f[i][2] f[i][0],f[i][1],f[i][2]表示这三种状态,三种状态的转移方程如下:
f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 2 ] − w [ i ] ) f[i][0] = max(f[i - 1][0], f[i - 1][2] - w[i]) f[i][0]=max(f[i1][0],f[i1][2]w[i])

f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] + w [ i ] f[i][1] = f[i - 1][0] + w[i] f[i][1]=f[i1][0]+w[i]

f [ i ] [ 2 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 1 ] [ 2 ] ) f[i][2] = max(f[i - 1][1], f[i - 1][2]) f[i][2]=max(f[i1][1],f[i1][2])

然后就是考虑初始化的问题,前两个状态在刚开始时都是非法状态,即 f [ 0 ] [ 0 ] , f [ 0 ] [ 1 ] f[0][0],f[0][1] f[0][0],f[0][1];只要将 f [ 0 ] [ 2 ] f[0][2] f[0][2]初始化为 0 0 0即可。

还有一个情况就是数据是单调下降的,也就是股票一直在跌,最优解就是什么都不做,因此最后的答案可能出现在 f [ n ] [ 1 ] f[n][1] f[n][1] f [ n ] [ 2 ] f[n][2] f[n][2]中。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010, inf = 0x3f3f3f3f;

int n;
int w[N];
int f[N][3];

int main()
{
    cin >> n;
    
    for(int  i = 1; i <= n; i ++) cin >> w[i];
    
    f[0][0] = f[0][1] = -inf;
    f[0][2] = 0;
    
    for(int i = 1; i <= n; i ++)
    {
        f[i][0] = max(f[i - 1][0], f[i - 1][2] - w[i]);
        f[i][1] = f[i - ][0] + w[i];
        f[i][2] = max(f[i - 1][1], f[i - 1][2]);
    }
    
   cout << max(f[n][1], f[n][2]) << endl;
    
    return 0;
}

Acwing 1052. 设计密码

你现在需要设计一个密码 S S S S S S需满足:

  • S S S的长度是 N N N
  • S S S只包含小写英文字母;
  • S S S不包含子串 T T T

例如, a b c abc abc a b c d e abcde abcde的子串, a b d abd abd不是 a b c d e abcde abcde的子串。

请问共有多少种不同的密码满足要求?

由于答案会非常大,请输出答案模 10 9 + 7 10^9 + 7 109+7的余数。

输入格式

第一行包含整数 N N N,表示密码的长度。

第二行输入字符串 T T T T T T中只包含小写字母。

输出格式

输出一个正整数,表示总方案数模 10 9 + 7 10^9 + 7 109+7后的结果。

数据范围

1 ≤ N ≤ 50 1 \le N \le 50 1N50,

1 ≤ ∣ T ∣ ≤ n 1 \le |T| \le n 1Tn ∣ T ∣ |T| T T T T的长度。

思路

这一题要用到基础课中的 K M P KMP KMP算法,最好去复习一下,链接在这,它是一个字符串匹配算法,目的是为了尽可能多的利用已知的信息进行匹配。暴力匹配的两个字符串的最坏情况是 O ( n ∗ m ) O(n * m) O(nm),而 K M P KMP KMP算法可以将其降到 O ( n + m ) O(n + m) O(n+m)。具体来说, K M P KMP KMP算法的核心是 n e x t next next数组,其存有的信息代表着:当模式串与文本串匹配不成功时,模式串应该向右移动到什么位置,即跳过一步一步向右移动的过程。需要注意的是 n e x t next next数组是对模式串而言的,而不是对待匹配的文本串。

n e x t next next数组的具体定义是: n e x t [ i ] next[i] next[i]表示模式串 P [ 0 ⋯ i − 1 ] P[0\cdots i - 1] P[0i1]的最长公共前后缀的长度。

根据题意,我们需要使用 26 26 26个小写字母构造一个长度为 n n n的字符串 S S S,且 T T T不是字符串的子串,那么每一位密码有 26 26 26种选择,因此最坏就有 26 n 26^n 26n种方案。

为了使构造的密码 S S S中不包含字符创 T T T,那就意味着要使匹配的过程无法到达 T T T的最后一位.

首先先通过 K M P KMP KMP处理出模式串 T T T n e x t next next数组,对于这道题的状态表示,使用 f [ i ] [ j ] f[i][j] f[i][j]表示已经构造了前 i i i位,且与模式串 T T T匹配到了 j j j位(很明显, j j j不能到 m = s t r l e n ( T ) m = strlen(T) m=strlen(T))。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N  =60, mod = 1e9 + 7;

int n;
int ne[N];
char str[N];
int f[N][N];

int main()
{
    cin >> n >> str + 1;
    
    for(int i = 2, j = 0; i <= n; i ++)
    {
        while(j && str[i] != str[j + 1]) j = ne[j];
        if(str[i] == str[j + 1]) j ++;
        ne[i] = j;
    }
    
    f[0][0] = 1;
    
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < m; j ++)
           	for(char k = 'a'; k <= 'z'; k ++)
            {
                int u = j;
                while(u && k != str[u + 1]) u = ne[u];
                if(k == str[u + 1]) u ++;
                f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod;
            }
    int res = 0;
    for(int i = 0; i < m; i ++) res = (res  + f[n][i]) % mod;
    cout <<res << endl;
    
    return 0;
}

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

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

相关文章

基于开源链动2+1模式AI智能名片S2B2C商城小程序的社群构建与新型消费迎合策略研究

摘要&#xff1a;随着个性化与小众化消费的崛起&#xff0c;消费者消费心理和模式发生巨大变化&#xff0c;社群构建对商家迎合新型消费特点、融入市场经济发展至关重要。开源链动21模式AI智能名片S2B2C商城小程序的出现&#xff0c;为社群构建提供了创新工具。本文探讨该小程序…

高性能RPC框架--Dubbo(五)

Filter&#xff1a; filter过滤器动态拦截请求&#xff08;request&#xff09;或响应&#xff08;response&#xff09;以转换或使用请求或响应中包含的信息。同时对于filter过滤器不仅适合消费端而且还适合服务提供端。我们可以自定义在什么情况下去使用filter过滤器 Activa…

搭建自己的语音对话系统:开源 S2S 流水线深度解析与实战

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

feign调用指定服务ip端口

1 背景 在springcloud开发时候&#xff0c;同时修改了feign接口和调用方的代码&#xff0c;希望直接在某个环境调用修改的代码&#xff0c;而线上的服务又不希望被下线因为需要继续为其他访问页面的用户提供功能后端服务&#xff0c;有时候甚者包含你正在修改的功能。 2 修改…

【深尚想!爱普特APT32F1023H8S6单片机重构智能电机控制新标杆】

在智能家电与健康器械市场爆发的今天&#xff0c;核心驱动技术正成为产品突围的关键。传统电机控制方案面临集成度低、开发周期长、性能瓶颈三大痛点&#xff0c;而爱普特电子带来的APT32F1023H8S6单片机无感三合一方案&#xff0c;正在掀起一场智能电机控制的技术革命。 爆款基…

Unity EventCenter 消息中心的设计与实现

在开发过程中&#xff0c;想要传递信号和数据&#xff0c;就得在不同模块之间实现通信。直接通过单例调用虽然简单&#xff0c;但会导致代码高度耦合&#xff0c;难以维护。消息中心提供了一种松耦合的通信方式&#xff1a;发布者不需要知道谁接收事件&#xff0c;接收者不需要…

MySQL远程连接10060错误:防火墙端口设置指南

问题描述&#xff1a; 如果你通过本机服务器远程连接MySQL&#xff0c;出现10060错误&#xff0c;那可能是你的防火墙的问题 解决&#xff1a; 第一步&#xff1a;查看防火墙规则 通过以下命令查询&#xff0c;看ports是否开放了3306端口&#xff0c;目前只开放了22端口 f…

使用 OpenCV 实现 ArUco 码识别与坐标轴绘制

&#x1f3af; 使用 OpenCV 实现 ArUco 码识别与坐标轴绘制&#xff08;含Python源码&#xff09; Aruco 是一种广泛用于机器人、增强现实&#xff08;AR&#xff09;和相机标定的方形标记系统。本文将带你一步一步使用 Python OpenCV 实现图像中多个 ArUco 码的检测与坐标轴…

canal实现mysql数据同步

目录 1、canal下载 2、mysql同步用户创建和授权 3、canal admin安装和启动 4、canal server安装和启动 5、java 端集成监听canal 同步的mysql数据 6、java tcp同步只是其中一种方式&#xff0c;还可以通过kafka、rabbitmq等方式进行数据同步 1、canal下载 canal实现mysq…

易境通专线散拼系统:全方位支持多种专线物流业务!

在全球化电商快速发展的今天&#xff0c;跨境电商物流已成为电商运营中极为重要的环节。为了确保物流效率、降低运输成本&#xff0c;越来越多的电商卖家选择专线物流服务。专线物流作为五大主要跨境电商物流模式之一&#xff0c;通过固定的运输路线和流程&#xff0c;极大提高…

06 如何定义方法,掌握有参无参,有无返回值,调用数组作为参数的方法,方法的重载

1.调用方法 2.掌握有参函数 3.调用数组作为参数 一个例题&#xff1a;数组参数&#xff0c;返回值 方法的重载 两个例题&#xff1a;冒泡排序和九九乘法表的格式学习

使用vscode MSVC CMake进行C++开发和Debug

使用vscode MSVC CMake进行C开发和Debug 前言软件安装安装插件构建debuug方案一debug方案二其他 前言 一般情况下我都是使用visual studio来进行c开发的&#xff0c;但是由于python用的是vscode&#xff0c;所以二者如果统一的话能稍微提高一点效率。 软件安装 需要安装的软…

提升开发运维效率:原力棱镜游戏公司的 Amazon Q Developer CLI 实践

引言 在当今快速发展的云计算环境中&#xff0c;游戏开发者面临着新的挑战和机遇。为了提升开发效率&#xff0c;需要更智能的工具来辅助工作流程。Amazon Q Developer CLI 作为亚马逊云科技推出的生成式 AI 助手&#xff0c;为开发者提供了一种新的方式来与云服务交互。 Ama…

@Column 注解属性详解

提示&#xff1a;文章旨在说明 Column 注解属性如何在日常开发中使用&#xff0c;数据库类型为 MySql&#xff0c;其他类型数据库可能存在偏差&#xff0c;需要注意。 文章目录 一、name 方法二、unique 方法三、nullable 方法四、insertable 方法五、updatable 方法六、column…

基于 ESP32 与 AWS 全托管服务的 IoT 架构:MQTT + WebSocket 实现设备-云-APP 高效互联

目录 一、总体架构图 二、设备端(ESP32)低功耗设计(适配 AWS IoT) 1.MQTT 设置(ESP32 连接 AWS IoT Core) 2.低功耗策略总结(ESP32) 三、云端架构(基于 AWS Serverless + IoT Core) 1.AWS IoT Core 接入 2.云端 → APP:WebSocket 推送方案 流程: 3.数据存…

unity在urp管线中插入事件

由于在urp下&#xff0c;打包后传统的相机事件有些无法正确执行&#xff0c;这时候我们需要在urp管线中的特定时机进行处理一些事件&#xff0c;需要创建继承ScriptableRenderPass和ScriptableRendererFeature的脚本&#xff0c;示例如下&#xff1a; PluginEventPass&#xf…

docker安装es连接kibana并安装分词器

使用Docker部署Elasticsearch、Kibana并安装分词器有以下主要优点&#xff1a; 1. 快速部署与一致性 一键式部署&#xff1a;通过Docker Compose可以快速搭建完整的ELK栈环境 环境一致性&#xff1a;确保开发、测试和生产环境完全一致&#xff0c;避免"在我机器上能运行…

线性回归中涉及的数学基础

线性回归中涉及的数学基础 本文详细地说明了线性回归中涉及到的主要的数学基础。 如果数学基础很扎实可以直接空降博文: 线性回归&#xff08;一&#xff09;-CSDN博客 一、概率、似然与概率密度函数 1. 概率&#xff08;Probability&#xff09; 定义&#xff1a;概率是描述…

如何计算VLLM本地部署Qwen3-4B的GPU最小配置应该是多少?多人并发访问本地大模型的GPU配置应该怎么分配?

本文一定要阅读我上篇文章&#xff01;&#xff01;&#xff01; 超详细VLLM框架部署qwen3-4B加混合推理探索&#xff01;&#xff01;&#xff01;-CSDN博客 本文是基于上篇文章遗留下的问题进行说明的。 一、本文解决的问题 问题1&#xff1a;我明明只部署了qwen3-4B的模型…

Attu下载 Mac版与Win版

通过Git地址下载 Mac 版选择对于的架构进行安装 其中遇到了安装不成功&#xff0c;文件损坏等问题 一般是两种情况导致 1.安装版本不对 2.系统权限限制 https://www.cnblogs.com/similar/p/11280162.html打开terminal执行以下命令 sudo spctl --master-disable安装包Git下载地…