作者推荐
本文涉及知识点
优化动态规划的时间复杂度,主要有如下几种:
一,不同的状态表示。
比如:n个人,m顶帽子。
第一种方式:dp[i][mask] ,i表示前i个人已经选择帽子,mask 表示 那些帽子已经选择。 空间复杂度:O(n2m)。
第二种方式:dp[i][mask] ,i表示前i个帽子已经选择,mask表示那些人已经选择。 空间复杂度:O(m22)。
n大,则现在方式一;否则选择方式二。
【状态压缩】【动态规划】【C++算法】1125.最小的必要团队
二,通过优化状态减少状态数
例一
num的长度∈ \in∈[1,1000],num[i]∈ \in∈[0,106] k∈ \in∈[0,1000]。
将num的元素放到两个数组中,两个数组的和都为k。
由于num[i] >=0,所以 数组和已经大于k 的无论如何都不会等于k,抛弃。
dp[k1][k2] 的状态数是固定。
当处理完 n u m [ 0 , i ) 时 , 两个数组的和是固定 ⟺ k 1 + k 2 ≡ ∑ j : 0 i − 1 n u m s [ j ] 当处理完num[0,i)时,两个数组的和是固定 \iff k1+k2 \equiv \sum\Large_{j:0}^{i-1} nums[j]当处理完num[0,i)时,两个数组的和是固定⟺k1+k2≡∑j:0i−1nums[j]
我记录k1或k2就可以了。新问题是k1 可能是5e8。
{ k 1 k 1 < k − m i n ( k 2 , k ) e l s e \begin{cases} k1 & k1 <k \\ -min(k2,k) & else \\ \end{cases}{k1−min(k2,k)k1<kelse
例子二
付费工人,各任务用时time[i],免费工人用时1,time.length∈ \in∈[1,500]。付费工人用时和必须大于等于免费工人用时。如果分别记录付费工人和免费工人用时,则状态数:500*500。
付费工人用时和必须大于等于免费工人 ⟺ \iff⟺ (statu = 付费工人用时 - 免费工人用时) >= 0
statu ∈ \in∈ [-500,500] 可以记录状态的时候+500,解析状态的时候再-500。
三 通过优化转移方程
转移方程主要有两种:
a,枚举前置状态,更新后置状态。除剪枝小幅提升性能外,暂时没发现优化方法。
b,枚举后置状态,通过前置状态计算后置状态。利用前缀和、极值、优先队列(堆)、单调栈(队列、向量)、预处理 等优化。
2617 网格图中最少访问的格子数 | 两种方法:分别用单调栈、优先队列优化 |
【动态规划】【滑动窗口】【C++算法】 629K 个逆序对数组 | 前缀和 |
【动态规划】【状态压缩】【2次选择】【广度搜索】1494. 并行课程 II | 枝小幅提升性能 |
【动态规划】【C++算法】1563 石子游戏 V | 极值 |
四 匹配无限次可以拆分成匹配0次和1次
以通配符为例。
abc 匹配 *
初始匹配长度0
处理* :
长度0的后置状态:*不匹配任何字符,匹配长度0。
长度0的后置状态:*匹配一个字符,匹配长度1。
长度1的后置状态:*不匹配任何字符,匹配长度1。
长度1的后置状态:*匹配一个字符,匹配长度2。
⋮ \quad \quad \vdots⋮
总计: *可以匹配0到无限字符,才可以这样处理。 .只能匹配一个字符不能这样处理。
【动态规划】【数学】【C++算法】1449. 数位成本和为目标值的最大数字
五 逆向思考
【动态规划】【 矩阵】【逆向思考】C++算法174地下城游戏
正向思考:要记录进入(r,c)后的健康,还有记录初始健康。比如:路径一: 3 → \rightarrow→ -2 ,初始只需要1,最终健康2。
路径二: -1 → \rightarrow→ 4 ,初始要求 2,最终健康度 5。如果终点格是-1,前者能过。 如果是-4,后者能过。前者需要4,才能过。
{ 路径一初始 1 ,路径二初始 2 终点 < − 1 路径一初始 4 ,路径二初始 2 终点 − 4 \begin{cases} 路径一初始1,路径二初始2 & 终点< -1 \\ 路径一初始4,路径二初始2 & 终点-4 \\ \end{cases}{路径一初始1,路径二初始2路径一初始4,路径二初始2终点<−1终点−4