You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
python/TangDou/AcWing/DP/ConvexHullTrick/【学习笔记】动态规划—各种 DP 优化.md

5.0 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 【学习笔记】动态规划—各种 DP 优化

原文链接,致敬大神

【大前言】

个人认为贪心,dp 是最难的每次遇到题完全不知道该怎么办看了题解后又瞬间恍然大悟TAT。这篇文章也是花了我差不多一个月时间才全部完成。

【进入正题】

用动态规划解决问题具有 空间耗费大时间效率高 的特点,但也会有时间效率不能满足要求的时候,如果算法有可以优化的余地,就可以考虑时间效率的优化。

DP 时间复杂度的分析】

DP 高时间效率的关键在于它减少了 冗余 ,即不必要的计算或重复计算部分,算法的冗余程度是决定算法效率的关键。而动态规划就是在将问题规模不断缩小的同时,记录已经求解过的子问题的解,充分利用求解结果,避免了反复求解同一子问题的现象,从而减少 冗余

但是,一个动态规划问题很难做到完全消除 冗余

下面给出动态规划时间复杂度的决定因素:

时间复杂度 = 状态总数 × 每个状态转移的状态数 × 每次状态转移的时间

DP 优化思路】

一:减少状态总数

(1). 改进状态表示

(2). 选择适当的规划方向

二:减少每个状态转移的状态数

(1). 四边形不等式和决策的单调性

(2). 决策量的优化

(3). 合理组织状态

(4). 细化状态转移

三:减少状态转移的时间

(1). 减少决策时间

(2). 减少计算递推式的时间

(上述内容摘自 《动态规划算法的优化技巧》毛子青 ,想要深入了解其思想的可以去看看这篇写得 超级好 的论文。)

看到这里是不是已经感觉有点蒙了呢? 本蒟蒻总结了一个简化版本:

DP 三要点】

在推导 dp 方程时,我们时常会感到毫无头绪,而实际上 dp 方程也是有迹可循的,总的来说,需要关注两个要点:状态决策和转移。其中 状态最为关键 决策最为复杂

【状态】

关于 状态 的优化可以从很多角度出发,思维难度极其高,有时候状态选择的好坏会直接导致出现 爆零满分 的分化。

【决策】

状态 不同,决策 优化则有着大量模板化的东西,在各大书籍,文章上你都可以看到这样的话:只要是形如 XXX 的状态转移方程,都可以用 XXX 进行优化。

【转移】

转移 则指由最优决策点得到答案的转移过程,其复杂度一般较低,通常可以忽略,但有时也需要特别注意并作优化。

本文将会重点针对 决策 优化部分作一些总结,记录自己的感悟和理解。

QAQ

一:【矩阵优化 DP

updata 之后由于篇幅过大,已搬出。。。。。

【学习笔记】动态规划—矩阵递推加速

TODO 挖坑待填

补充:其实质是优化 转移

QAQ

二:【数据结构优化 DP

【前言】

在一些 dp 方程的状态转移过程中,我们通常需要在某个范围内进行择优,选出最佳决策点,这往往可以作为 dp 优化的突破口。

数据结构的使用较灵活,没有一个特定的模板,需要根据具体情况而定,选择合适的方案。由于状态转移总是伴随着 区间查询最值区间求和 等操作,即 动态区间操作,所以 平衡树 可以作为一个有用的工具,但考虑到代码复杂度,使用 树状数组 或者 线段树 将会是一个不错的选择。

其实质是优化 决策

1.【维护合适的信息】

The Battle of Chibi [UVA12983] 为例,大概题意就是计算在给定的序列中严格单调递增子序列的数量,并对 1e9+7 取模,给定序列长度小于等于 1000

方程应该是比较好推的,用 dp[i][j] 表示由序列中在 j 之前的数构成并以 a[j] 结尾的子序列中,长度为 i 的子序列的数量。则:

\large dp[i][j]=\sum dp[i1][k]

Q:状态用一维表示行不行? A:如果是一维表示,那么就是表示由序列中在 j 之前的数构成并以 a[j] 结尾的子序列的数量。这时考虑一下状态转移,看看它的前序状态是怎么样转移过来的:枚举它前面所有的数字,a[k]<a[j],这样不行吗? 不行,比如1~ 2~ 2~ 2~ 6 ~ 3~ 3~ 4,因为存在重复数字2,会造成统计重复,无法去重。 办法就是增加一个维度,还要记录以k结尾时的子序列长度,就解决了去重的问题。

对于决策点 dp[i1][k] 这里出现了 3 个信息:

  1. 在原序列中的位置 k<j
  2. a[k]<a[j]
  3. dp[i1][k] 的和。