开通博客园
注意
1.0 学习
测试
- 1
-
- 和的
动态规划
包括线性DP(数字三角形模型、最长上升子序列模型、编辑距离)、背包模型(01背包、完全背包、多重背包、分组背包、混合背包...)、状态机、状态压缩DP、区间DP(石子合并..)、树形DP、数位DP、单调队列优化DP、斜率优化DP等内容
动态规划
传统dp:
关键词:状态表示、状态转移、最优子结构、重叠子问题、后效性
DP问题一般是求若干有限集合中的最值问题,并且此类问题存在子问题重叠(Overlap-subproblems),如果通过暴力枚举每种可能解决问题,那么将会有大量重复的计算;如果从集合角度来分析DP问题,每次枚举一个集合并且利用子问题重叠特性,将子问题的解存储下来,可以大大提高效率;
DP问题的核心是状态集合 f(i) 的定义,在状态及转移计算中,划分子集的依据:寻找最后一个不同点,即处理最后一个子集的选择。
闫氏DP分析法
从集合角度考虑DP问题,又称闫氏分析法,包含以下两步:
- 状态表示——集合以及属性(Max/Min/数量/存在性);
- 状态计算——集合的划分。一个重要的划分依据:“最后一步”;集合划分原则:可重但不漏。
数字三角形模型
此类问题中:
- 状态表示
- 集合:从(1,1)移动到(i,j)的所有路线
- 属性:最大/大小
- 状态计算
- 状态计算:集合划分->最后一步移动方向
- 摘花生
- 求最大值,初始化为0即可。
- 最低通行费
- 求最小值,初始化为
0x3f3f3f3f
- 注意对于首行首列的处理
- 求最小值,初始化为
- 方格取数
- 走两次
-
与前两题相比,本题“共走两次”,且每一个数最多只能选一次。可根据前述速录,分两次分开来求,再判断两次是否到达相同格子。减去重复格子即可。
- 维度压缩
- 四维表示:
f[i1,j1,i2,j2]
:表示从(1,1)
分别走到(i1,j1)
,(i2,j2)
的路径上能取到的数的最大值。
根据“最后一步”可划分为四种情况。每次均可向右向下组合。 - 三维表示:关键在于 如果在(i,j)重合,必定有两者所走步数相等,此时
i1+j1==i2+j2==k
(是重合的必要条件); 设两次路径一起走,所走步数为k,则有第一次走到了(i1,k-i1)
,第二次走到(i2,k-i2)
;
此时设f[k,i1,i2]为从(1,1)分别走到(i1,k-i1),(i2,k-i1)代路径上能取到的最大值。
此时最后一步也是四种情况即,下下f[k-1,i1-1,i2-1]
,下右f[k-1,i1-1,i2]
,右下f[k-1,i1,i2-1]
,右右f[k-1,i1,i2]
;
- 四维表示:
- 拓展:k取方格数。方法是费用流中的最大流。动态规划题变成了图论?其实动态规划包含于图论,90%DP都能转化为最短路问题。
最长上升子序列模型
(LIS)Longest Increasing Subsequence
此类问题中:
- 状态表示
- 集合:所有以a[i]为结尾的严格单调上升子序列
- 属性:最大
- 状态计算
- 状态计算:集合划分->最后一个不同的点
-
- 动态规划:O(n2)
- 状态表示:f[i]表示从第一个数字开始算,以w[i]结尾的最大的上升序列。(以w[i]结尾的所有上升序列中属性为最大值的那一个)
- 状态计算(集合划分):j∈(0,1,2,..,i-1), 在w[i] > w[j]时,f[i] = max(f[i], f[j] + 1)。
- 有一个边界,若前面没有比i小的,f[i]为1(自己为结尾)。最后在找f[i]的最大值。
- (动态规划 + 二分) O(nlogn)
- 状态表示:f[i]表示长度为i的最长上升子序列,末尾最小的数字。(长度为i的最长上升子序列所有结尾中,结尾最小min的) 即长度为i的子序列末尾最小元素是什么。
- 状态计算:对于每一个w[i], 如果大于f[cnt-1] (下标从0开始,cnt长度的最长上升子序列,末尾最小的数字),那就cnt+1,使得最长上升序列长度+1,当前末尾最小元素为w[i]。 若w[i]小于等于f[cnt-1],说明不会更新当前的长度,但之前末尾的最小元素要发生变化,找到第一个 大于或等于 (这里不能是大于) w[i],更新以那时候末尾的最小元素。
- f[i]一定以一个单调递增的数组,所以可以用二分法来找第一个大于或等于w[i]的数字。
stl版本
- 动态规划:O(n2)
-
- 双向LIS 取最大
-
- 双向LIS 取和 -1即可
-
- 排序后LIS妙啊
-
最大上升子序列和
-
最长公共上升子序列LCS
-
- 1.贪心
- 如何证明两个数相等?A>=B A<=B
A表示贪心算法得到的序列个数,B;表示最优解;
B<=A, ok A<=B, 使用调整法
- 如何证明两个数相等?A>=B A<=B
- 2.贪心流程
- 从前往后扫描每个数,对于每个数:
情况1:如果现有的子序列的结尾都小于当前数,则创建新子序列。
情况2:将当前数放到结尾大于等于它的最小的子序列后面。
- 从前往后扫描每个数,对于每个数:
- 3.???反链实现???Dilworth原理
- 1.贪心
-
导弹防御系统(dfs)
背包模型
背包模型一般求解
- 最大值、最小值
- 方案数
- 具体方案: 最短路问题,即记录转移过程,不能再用状态压缩了?而且如果输出要求字典序最小的方案,则需要对i的遍历顺序变化!!??
初始化汇总:
背包问题中 体积至多是 j ,恰好是 j ,至少是 j 的初始化问题的研究
学背包问题的过程
1、一开始学背包问题时遇到的大多数的状态表示是:从前i个物品中选,且总体积不超过j的问题。
2、慢慢地在提高课中,就有出现状态表示是:从前i个物品中选,且总体积恰好是j的问题。例如 AcWing 1023. 买书 ,求的是恰好是j的总方案数问题。
3、同时还出现了状态表示是:从前i个物品中选,且总体积至少是j的问题。例如 AcWing 1020. 潜水员 ,求的是总体积至少是j的最小价值
可以观察到,他们的分析方法以及状态转移方程都是一样的,唯独是初始化有很大的不同
- 求方案数初始化总结
-
二维情况
1、体积至多j,f[0][i] = 1, 0 <= i <= m,其余是0
2、体积恰好j,f[0][0] = 1, 其余是0
3、体积至少j,f[0][0] = 1,其余是0 -
一维情况
1、体积至多j,f[i] = 1, 0 <= i <= m,
2、体积恰好j,f[0] = 1, 其余是0
3、体积至少j,f[0] = 1,其余是0
-
求最大值最小值初始化总结
- 二维情况
1、体积至多j,f[i,k] = 0,0 <= i <= n, 0 <= k <= m(只会求价值的最大值)
2、体积恰好j,
当求价值的最小值:f[0][0] = 0, 其余是INF
当求价值的最大值:f[0][0] = 0, 其余是-INF
3、体积至少j,f[0][0] = 0,其余是INF(只会求价值的最小值)
- 01背包问题(滚动数组)
每个物品最多只能放一次。 - 完全背包问题(优化成二维,以及滚动数组)
每种物品可以放无限多次。(空间优化为1维后,只有完全背包是正序遍历) - 多重背包问题(倍增优化NVlogS成01背包、单调队列优化)
每种物品有一个固定的次数上限。 - 混合三种背包问题
将前面三种简单的问题叠加成较复杂的问题。 - 二维费用的背包问题
一个简单的常见扩展。 - 分组的背包问题
每组里面只能选一种。后两节的基础。 - 有依赖的背包问题
另一种给物品的选取加上限制的方法。 - 泛化物品
我自己关于背包问题的思考成果,有一点抽象。 - 背包问题问法的变化
试图触类旁通、举一反三。
1.01背包问题
有N件物品和一个容量为V的背包。第i件物品的费用是v[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
f[i][j]=max{f[i-1][j],f[i-1][j-v[i]]+w[i]}
“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][j];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为j-v[i]的背包中”,此时能获得的最大价值就是f[i-1][j-v[i]]再加上通过放入第i件物品获得的价值w[i]。
优化
以上时间和空间复杂度均为O(N*V)
可把空间优化为O(V)
说明:
其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相当于我们的转移方程f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]},因为现在的f[v-c[i]]就相当于原来的f[i-1][v-c[i]]。如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了f[i][v]由f[i][v-c[i]]推知,与本题意不符,但它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维数组解01背包问题是十分必要的。
关于初始化:
- “恰好装满背包”时的最优解
- 要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞
- 小于容量的最优解
- 初始化时应该将f[0..V]全部设为0
2. 完全背包问题
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
f[i][j]=max{f[i-1][j-k*v[i]]+k*w[i] | 0<=k*c[i]<=v}
可优化为
f[i][j]=max{f[i-1][j], f[i-1][j-v[i]]+w[i]}
具体代码为:
滚动数组优化后代码为
f[j] = max(f[j], f[j-v[i]] + w[i]);
具体代码为:
对比01背包:
f[i][j]=max{f[i-1][j], f[i-1][j-v[i]]+w[i]}
具体代码为:
优化后为
综上:
滚动优化前 两者相差一点:
f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);//01背包
f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);//完全背包问题
滚动优化后 两者代码相同:
f[j] = max(f[j],f[j-v[i]]+w[i]);//01背包 倒序啊!
f[j] = max(f[j],f[j-v[i]]+w[i]);//完全背包问题
分组倍增优化
完全背包也能进行分组倍增优化,每种物品的最大数量为j/v[i],但优化后复杂度不如单调队列优化,所有暂不用该优化。此优化可用于多重背包问题中。见下文。
3. 多重背包问题
方法:
- 暴力 转化成01背包
- 分组倍增优化 转化成01背包
- 最大长度的队列优化,即滑动窗口最大值,模型为单调队列(难)
分组倍增优化(二进制优化)
对于每种物品,均执行:
对于i种物品,最多用s个。此时把s拆分成logs组,1,2,4,...2^logs,每组选或不选,最终能凑成选择0~s个的任意值。将原始物品删除,用新的物品代替其体积和价值。转化为01背包。
将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2(k-1),n[i]-2k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。
分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示,这个证明可以分0..2k-1和2k..n[i]两段来分别讨论得出.
这样就将第i种物品分成了O(log n[i])种物品,将原问题转化为了复杂度为O(V*Σlog n[i])的01背包问题.
未优化代码:
优化过程:
在完全背包中,通过两个状态转移方程:
再来看下多重背包,
优化后代码:
解析:
- 二进制优化,它为什么正确,为什么合理,凭什么可以这样分??
二进制优化,它为什么正确,为什么合理,凭什么可以这样分??
我们首先确认三点:
(1)我们知道转化成01背包的基本思路就是:判断每件物品我是取了你好呢还是不取你好。
(2)我们知道任意一个实数可以由二进制数来表示,也就是20~2k其中一项或几项的和。
(3)这里多重背包问的就是每件物品取多少件可以获得最大价值。
分析:
如果直接遍历转化为01背包问题,是每次都拿一个来问,取了好还是不取好。那么根据数据范围,这样的时间复杂度是O(n^3),也就是1e+9,这样是毫无疑问是会TLE的。
假如10个取7个好,那么在实际的遍历过程中在第7个以后经过状态转移方程其实已经是选择“不取”好了。现在,用二进制思想将其分堆,分成k+1个分别有2k个的堆,然后拿这一堆一堆去问,我是取了好呢,还是不取好呢,经过dp选择之后,结果和拿一个一个来问的结果是完全一样的,因为dp选择的是最优结果,而根据第二点任意一个实数都可以用二进制来表示,如果最终选出来10个取7个是最优的在分堆的选择过程中分成了20=1,21=2,22=4,10 - 7 = 3 这四堆,然后去问四次,也就是拿去走dp状态转移方程,走的结果是第一堆1个,取了比不取好,第二堆2个,取了比不取好,第三堆四个,取了比不取好,第四堆8个,取了还不如不取,最后依旧是取了1+2+4=7个。
Tip:参考博客
如果仍然不是很能理解的话,取这样一个例子:要求在一堆苹果选出n个苹果。我们传统的思维是一个一个地去选,选够n个苹果就停止。这样选择的次数就是n次
二进制优化思维就是:现在给出一堆苹果和10个箱子,选出n个苹果。将这一堆苹果分别按照1,2,4,8,16,.....512分到10个箱子里,那么由于任何一个数字x ∈[1,1024]
都可以从这10个箱子里的苹果数量表示出来,但是这样选择的次数就是 ≤10次 。
这样利用二进制优化,时间复杂度就从O(n3)降到O(n2logS),从4*109降到了2*107。
单调队列优化
多重背包III
关键词: 单调队列、斜率优化、
4. 二维费用的背包问题
空间优化后同一维费用,每个费用均采取倒序方式。
5. 分组背包
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。si为第i组的物品种类数量。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
...
状态机
状态压缩
区间DP
树形DP
数位DP
单调队列优化DP
斜率优化DP
__EOF__

本文链接:https://www.cnblogs.com/afengcodes/p/15837189.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效