动态DP入门线性动态DP

动态DP入门&线性动态DP

  • 前言
  • 核心思想
  • 例1
  • 例2
  • 2024牛客寒假4K
  • 2022牛客寒假2J
  • 结论

前言

OI-WiKi上有一个动态DP讲解,直接讲到了树型DP领域,同时需要树链剖分,门槛有点高。本文针对线性DP做一个动态DP的讲解。

首先当然要懂得一定的DP的相关知识,然后需要知道DP方程的矩阵表达。可以看这里——根据递推公式构造系数矩阵用于快速幂。很多DP的状态转移方程都可以写成矩阵形式,由此就有了矩阵快速幂优化和动态DP的基础。特别是本文专门举例的线性DP。

核心思想

常规的DP方程一般形如:

D i = f ( D i − 1 ) D_i=f(D_{i-1}) Di=f(Di1)

D i D_i Di D i − 1 D_{i-1} Di1的某个函数。与矩阵快速幂优化类似,想办法将DP方程改为:
[ D i 0 或 1 ] = M i × [ D i − 1 0 或 1 ] \begin{bmatrix} D_i \\ 0或1 \end{bmatrix}=M_i\times\begin{bmatrix} D_{i-1} \\ 0或1 \end{bmatrix} [Di01]=Mi×[Di101]

其中 M i M_i Mi表示 i i i位置的系数矩阵。这里用到了矩阵乘法,不过不一定是乘法,只是类乘操作而已,即满足某种性质的操作。令上述包含 D i D_i Di的向量记为 X i X_i Xi,则方程改为
X i = ∏ j = 1 i M j × X 0 X_i=\prod_{j=1}^{i}{M_j}\times{X_0} Xi=j=1iMj×X0

于是求 X i X_i Xi转化为区间矩阵求积的操作,这个区间操作可以使用线段树维护。同时很自然的也就支持了修改操作。

例1

首先看一个完全不需要动态DP的例子。给定一个数组,单点修改,区间求和。

规划方程: D i = A i + D i − 1 D_i=A_{i}+D_{i-1} Di=Ai+Di1

写成矩阵形式:

[ D i 1 ] = [ 1 A i 0 1 ] × [ D i − 1 1 ] \begin{bmatrix} D_i \\ 1 \end{bmatrix}=\begin{bmatrix} 1 & A_i \\ 0 & 1 \end{bmatrix}\times\begin{bmatrix} D_{i-1} \\ 1 \end{bmatrix} [Di1]=[10Ai1]×[Di11]

因此第 i i i个系数矩阵就是

[ 1 A i 0 1 ] \begin{bmatrix} 1 & A_i \\ 0 & 1 \end{bmatrix} [10Ai1]

而且此处用的就是正常的矩阵乘法。用线段树可以轻松维护其区间积与单点修改操作(修改某个点 A i A_i Ai,就是修改第 i i i个系数矩阵)。对于区间查询 [ l , r ] [l,r] [l,r],只需要计算:
a n s = ( ∏ i = l r [ 1 A i 0 1 ] ) × [ 0 1 ] ans=\big(\prod_{i=l}^{r}\begin{bmatrix} 1 & A_i \\ 0 & 1 \end{bmatrix}\big)\times\begin{bmatrix} 0 \\ 1 \end{bmatrix} ans=(i=lr[10Ai1])×[01]
即可, a n s [ 1 ] ans[1] ans[1]即答案。
当然,如果进一步推敲的话,可以发现这个线段树本质上就是维护的 A i A_i Ai的和。这是一个实现上毫无必要的、但很好的仅供学习的例子,如果后面的例子有难度的话。

例2

再看一个复杂一点点的例子。给定一个数组,查询区间最大子段和,单点修改。首先这个问题仍然可以直接使用线段树解决。其次,来看看动态DP的做法。

U i U_i Ui是以 i i i结尾的最大子段和, V i V_i Vi [ 1 , i ] [1,i] [1,i]区间的最大子段和,则规划方程是:

U i = max ⁡ ( A i , A i + U i − 1 ) V i = max ⁡ ( U i , V i − 1 ) U_i=\max{(A_i,A_{i}+U_{i-1})} \\ V_i=\max{(U_i, V_{i-1})} Ui=max(Ai,Ai+Ui1)Vi=max(Ui,Vi1)

首先修改其中一个DP方程为: V i = max ⁡ ( A i , A i + U i − 1 , V i − 1 ) V_i=\max{(A_i,A_{i}+U_{i-1},V_{i-1})} Vi=max(Ai,Ai+Ui1,Vi1)

然后写成矩阵形式:
[ U i V i 0 ] = [ A i − ∞ A i A i 0 A i − ∞ − ∞ 0 ] × [ U i − 1 V i − 1 0 ] \begin{bmatrix} U_i \\ V_i \\ 0 \end{bmatrix}=\begin{bmatrix} A_i & -\infty & A_i \\ A_i & 0 & A_i \\ -\infty & -\infty & 0 \end{bmatrix}\times\begin{bmatrix} U_{i-1} \\ V_{i-1} \\ 0 \end{bmatrix} UiVi0 = AiAi0AiAi0 × Ui1Vi10

这里的 × \times ×表示矩阵的类乘操作或者说是矩阵的广义乘法操作,定义如下:令矩阵 C = A × B C=A\times{B} C=A×B,则
C i , j = max ⁡ k = 1 3 ( A i , k + B k , j ) C_{i,j}=\max_{k=1}^{3}{(A_{i,k}+B_{k,j})} Ci,j=k=1max3(Ai,k+Bk,j)

写成单行、单列的形式即:

[ a 1 a 2 a 3 ] × [ b 1 b 2 b 3 ] = max ⁡ ( a 1 + b 1 , a 2 + b 2 , a 3 + b 3 ) \begin{bmatrix}a_1 & a_2 & a_3\end{bmatrix}\times\begin{bmatrix} b_1 \\ b_2 \\ b_3 \end{bmatrix}=\max{(a_1+b_1,a_2+b_2,a_3+b_3)} [a1a2a3]× b1b2b3 =max(a1+b1,a2+b2,a3+b3)

因此
[ A i − ∞ A i A i 0 A i − ∞ − ∞ 0 ] \begin{bmatrix} A_i & -\infty & A_i \\ A_i & 0 & A_i \\ -\infty & -\infty & 0 \end{bmatrix} AiAi0AiAi0
就是第 i i i个矩阵,一共要维护 N N N个矩阵。修改操作也很容易维护,改变 A i A_i Ai就是改变第 i i i个矩阵。区间查询操作 [ l , r ] [l,r] [l,r],就是计算:

a n s = ( ∏ i = l r [ A i − ∞ A i A i 0 A i − ∞ − ∞ 0 ] ) × [ − ∞ − ∞ 0 ] ans=\big(\prod_{i=l}^{r}\begin{bmatrix} A_i & -\infty & A_i \\ A_i & 0 & A_i \\ -\infty & -\infty & 0 \end{bmatrix}\big)\times\begin{bmatrix} -\infty \\ -\infty \\ 0 \end{bmatrix} ans=(i=lr AiAi0AiAi0 )× 0

a n s [ 1 ] ans[1] ans[1]就是答案,因为 a n s [ 1 ] ans[1] ans[1]对应了规划目标 V V V

2024牛客寒假4K

同样,这个题目可以不用动态DP,直接用线段树维护相关信息即可。

题目大意:给定一个字符串仅包含YBR,表示命令。初始位于0位置且右边无边界限制。命令执行如下:

  1. Y指令表示在当前位置添加一个块;
  2. B指令表示先右移一格到达一个新位置,再在这个新位置添加一个块;
  3. R指令表示先将当前位置的块倍增一下,再添加一个块(假设当前位置本来有3块,则该指令过后变成7块)。

还有 Q Q Q个操作,一共分两类:

  1. p c: 将第p个位置的命令改为c;
  2. s e: 从零开始,依次执行 [ s , e ] [s,e] [s,e]区间的命令,问最后的块数。

对每个操作2,输出答案。

D i D_i Di是第 i i i个操作后当前位置的块数(注意,不一定是第 i i i个位置,因为一个命令不一定新增一个位置,不过这个并没有影响)。令 S i S_i Si是第 i i i个操作后总块数。令动态规划的列向量是:
[ D i S i 1 ] \begin{bmatrix} D_{i} \\ S_{i} \\ 1 \end{bmatrix} DiSi1 简单推理一下就可以得到YBR三个命令分别对应的三个系数矩阵是:
[ 1 0 1 0 1 1 0 0 1 ] , [ 0 0 1 0 1 1 0 0 1 ] , [ 2 0 1 1 1 1 0 0 1 ] \begin{bmatrix} 1 & 0 & 1 \\ 0 & 1 & 1 \\ 0 & 0 & 1 \end{bmatrix}, \begin{bmatrix} 0 & 0 & 1 \\ 0 & 1 & 1 \\ 0 & 0 & 1 \end{bmatrix}, \begin{bmatrix} 2 & 0 & 1 \\ 1 & 1 & 1 \\ 0 & 0 & 1 \end{bmatrix} 100010111 , 000010111 , 210010111

因此可以得到一个矩阵的数组,维护这个矩阵数组的区间积就行,支持单点修改。这里用到的就是正经的矩阵乘法。当然这里会注意到系数矩阵连乘展开以后,其实是倒序的。前面的两个例子之所以没有这个问题,是因为例1中的矩阵形式比较特殊,因此支持乘法的交换律。而例2中的操作本身就支持交换律。所以那个两个例子都可以直接按顺序维护。这个例子则是正经的矩阵乘法,不支持交换律。因此倒过来维护即可。

2022牛客寒假2J

题目大意:有三种魔法球、十种技能,每种技能都是三种魔法球的组合(可重复)。现在需要连续释放技能,但是手中只能持有三个魔法球,且必须按照队列性质。
例如需要连续释放技能1和2,则首先持有三个魔法球,记作
a1 a2 a3
释放技能1。随后,如果a1a2a3不是技能2的组合,则必须先拿掉a1, 追加a4(可以自由选择追加的魔法球)。
此时手中持有变为了:a2a3a4
如果能满足施法,即可完成。
否则必须扔掉a2,追加a5。
还不行的话就扔掉a3,追加a6。此时必然可以释放技能2。

手中持有必须按顺序,但是技能构成无需按顺序。
例如假设输入给定技能1的构成是a3a2a1,则手中持有a1a2a3一样可以释放技能1

现在给定N个技能的序列,两种操作:

  1. s e: 问连续释放[s, e]区间内的技能,最少需要依次拿几个魔法球,每次施法之初手中都是空的
  2. p x: 将第p个位置的技能修改为x

首先释放第一个技能必须拿3个球,然后考虑后续。
从技能a到技能b,需要追加的球的数量显然与a魔法球以及b魔法球的排列有关。
一个技能最多可能存在6种不同的排列。
因此令 D i , j , u , v D_{i,j,u,v} Di,j,u,v 记录技能 i i i的排列 u u u后面跟技能 j j j的排列 v v v时需要改变的魔法球的数量。
所以D是一个 10 × 10 × 6 × 6 10\times10\times6\times6 10×10×6×6的四维数组,可以预处理出来。

再考虑一段连续的释放,首先考虑 [ 1... n ] [1...n] [1...n],令 Z n u Z_{nu} Znu表示从1释放到 n n n的最少数量,且以 n n n u u u排列结尾。
则:
Z n u = min ⁡ { Z n − 1 , v + D n − 1 , n , v , u , v 是 n − 1 的所有排列状态 } Z_{nu} = \min\{Z_{n-1,v} + D_{n-1,n,v,u}, v是n-1的所有排列状态\} Znu=min{Zn1,v+Dn1,n,v,u,vn1的所有排列状态}
min ⁡ { Z n u , u 是 n 的所有排列 } \min\{Z_{nu}, u是n的所有排列\} min{Znu,un的所有排列}为所求。
Z n Z_n Zn看作是列向量,将 D i j D_{ij} Dij看做是矩阵,可以将规划方程写成矩阵形式
Z n = D n − 1 , n × Z n − 1 Z_n = D_{n-1,n}\times{Z_{n - 1}} Zn=Dn1,n×Zn1

同样,这里是用的是类乘操作,与例2中类似,只不过这里取的是最小值。同时注意到这个操作是支持交换律的,因此按正序维护即可。

题目给定了 N N N个技能,则一共需要 N − 1 N-1 N1次转换,因此维护 N − 1 N-1 N1个矩阵即可。

结论

感觉上线性动态DP是不需要的,因为似乎可以直接设计线段树,维护相关信息完成。不过从线性DP入手动态DP,不失为一个好的学习路线。另一方面,动态DP可以提供一个统一的模板,构思简单,实现高度一致,在常数时间充足的情况下,值得一试。

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

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

相关文章

【qt创建线程两种方式】

QT使用线程的两种方式 1.案例进度条 案例解析: 如图由组件一个进度条和三个按钮组成,当点击开始的时候进度条由0%到100%,点击暂停,进度条保持之前进度,再次点击暂停变为继续,点击停止按钮进度条停止。 案…

从可靠性的角度理解 tcp

可靠性是 tcp 最大的特点。常见的用户层协议,比如 http, ftp, ssh, telnet 均是使用的 tcp 协议。可靠性,即从用户的角度来看是可靠的,只要用户调用系统调用返回成功之后,tcp 协议栈保证将报文发送到对端。引起不可靠的表现主要有…

【大模型 知识图谱】ChatKBQA:KBQA知识图谱问答 + 大模型

ChatKBQA:KBQA知识图谱问答 大模型 提出背景传统方法处理流程ChatKBQA处理流程对比优势 总结ChatKBQA框架概览特征1:逻辑形式生成特征2:无监督实体和关系检索特征3:参数高效的微调特征4:GQoT 可解释的查询执行特征5&a…

《ShardingSphere JDBC?Sharding JDBC?》基本小白脱坑问题

因为在短链接中的很多操作也需要依靠sharding JDBC来完成所以同时也在短链接的文章中。 在网上看了很多文章,可能是因为技术的迭代等等原因,看的越多蒙的越快。在学习的道路上梳理一下,希望可以帮助到别的小伙伴。 官网地址: A…

git stash 正确用法

目录 一、背景 二、使用 2.1 使用之前,先简单了解下 git stash 干了什么: 2.2 git stash 相关命令 2.3 使用流程 1. 执行 git stash 2. 查看刚才保存的工作进度 git stash list 3. 这时候在看分支已经是干净无修改的(改动都有暂存到 stash) 4. 现在…

Fusion OS

1 VMware workstation搭建华为FusionCompute实验环境 VMware workstation搭建华为FusionCompute实验环境(三)保姆级安装教程,可运行虚拟机-CSDN博客 在VMware Workstation以及云服务器上安装华为FusionCompute_vm安装华为存储模拟软件-CSDN…

C# ICloneable的特点及其用法

在C#中,ICloneable 是一个接口,用于定义允许一个对象创建自身的精确副本,即“克隆”的能力。ICloneable 接口只包含了一个方法:Clone,该方法在实现时应该返回当前对象的一个副本。 这里是 ICloneable 接口的定义&…

git提交代码冲突

用idea2023中的git提交代码,出现 error: Your local changes to the following files would be overwritten by merge: ****/****/****/init.lua Please commit your changes or stash them before you merge. Aborting 出现这个错误可能是因为你的本地修改与远…

Leetcode 3039. Apply Operations to Make String Empty

Leetcode 3039. Apply Operations to Make String Empty 1. 解题思路2. 代码实现 题目链接:3039. Apply Operations to Make String Empty 1. 解题思路 这一题的话其实挺简单的,想清楚的话其实最后一轮遗留的字符必然是出现频率最高的一个或多个字符的…

SpringBoot常见问题

1 引言 Spring Boot是一个基于Spring框架的快速开发脚手架,它简化了Spring应用的初始化和搭建过程,提供了众多便利的功能和特性,比如自动配置、嵌入式Tomcat等,让开发人员可以更加专注于业务逻辑的实现。   Spring Boot还提供了…

单机启动/开机启动SpringBoot服务的正确方式

此操作只针对于测试环境或单机部署的情况下,使用Jenkins自动化部署或docker部署SpringBoot服务请忽略。 SpringBoot单机启动和集群启动的区别: 部署方式:单机启动可以直接运行jar文件或使用IDE启动应用程序,而双机集群启动需要将…

[鸿蒙开发]一款便捷开发的本地关系数据操作框架

# cjdb## 简介_cjdb 是一套简化数据库操作,面相程序开发的简洁架构,正在不断完善中,欢迎各位大佬指正_#### CJDBUtil API1. init(config: { context: any, dbName?: string, securityLevel?: relationalStore.SecurityLevel }): Promise<void> 2. query\<T>(c…

MySQL全量备份

一、实验素材 1.创建student和score表 (1) student表 create database school; use schoolCREATE TABLE student ( id INT(10) NOT NULL UNIQUE PRIMARY KEY , name VARCHAR(20) NOT NULL , sex VARCHAR(4) , birth YEAR, department VARCHAR(20) , address VARCHAR(50) );(…

C++项目 -- 高并发内存池(五)释放内存过程

C项目 – 高并发内存池&#xff08;五&#xff09;释放内存过程 文章目录 C项目 -- 高并发内存池&#xff08;五&#xff09;释放内存过程一、Thread Cache释放内存1.完善FreeList功能2.Thread Cache释放内存 二、Central Cache释放内存三、Page Cache释放内存四、释放内存过程…

Pytorch关于CIFAR-10测试

下载 CIFAR-10数据集&#xff1a; 官网&#xff1a;https://www.cs.toronto.edu/~kriz/cifar.html CIFAR-10的网络结构&#xff1a; import torch from torch import nn from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential#定义网络结构 class Model(nn.Mo…

shell脚本控制模板判断

读取 ip文件列表 执行脚本 #!/bin/bash declare hj$1 echo $hj function duqu01(){ while :;do cat /root/wwd/0206/ip_list01|while read m s;do declare $m declare $s echo ‘’ echo $mastet’asdasdasdasd’ echo $slave’bbbbbbbbbb’ done #exit 0 done } function duq…

【社区投稿】Rust登陆华为鸿蒙操作系统之Native模块开发

Rust登陆【华为鸿蒙】操作系统之Native模块开发 名词解释 【鸿蒙操作系统】的英文全名是Open Harmony Operation System。正文将以其首字母缩写词ohos引用该词条。【鸿蒙软件开发工具包】的英文全名是Open Harmony Software Development Kit。正文也将以它的首字母缩写词ohsdk引…

【矩阵】托普利茨矩阵

每日一道算法题之托普利茨矩阵 一、题目描述二、思路三、C代码 一、题目描述 题目来源&#xff1a;LeetCode 给你一个 m x n 的矩阵 a 。如果这个矩阵是托普利茨矩阵&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果矩阵上每一条由左上到右下的对角线上…

SQL查询数据是否存在

SQL查询是否存在 count优化总结 根据某一条件从数据库表中查询是否有数据存在。 大多数时候&#xff0c;我们习惯性使用count。 count 大多数时候&#xff0c;我们在业务中会“肌肉记忆”&#xff0c;使用count来做这样的需求 mapper.xml中&#xff1a; <select id"c…

C++-带你初步走进继承(1)

1.继承的概念及定义 1.1继承的概念 继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段&#xff0c;它允许程序员在 保 持原有类特性的基础上进行扩展 &#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承 呈现了面向对象 …