斜率优化DP 总结(含凸优化)

本文深入探讨了斜率优化的DP形式及其转换为斜率式的方法,便于进行二分查找。同时,介绍了凸优化的概念及在特定条件下的应用,通过调整参数K来优化分组数量,给出了具体的实例问题。

我看了很多%d%a%l%a%o的博客,使我对其印象深刻。
确实,斜率优化的DP式一般都是:
f [ i ] = m i n f [ j ] + a [ i ] ∗ a [ j ] + . . . f[i]=min{f[j]+a[i]*a[j]}+... f[i]=minf[j]+a[i]a[j]+...
我们对于k<j<i,假设从j转移比从k转移要优,我们可以得到以下式子:
f [ j ] + a [ i ] ∗ a [ j ] &lt; f [ k ] + a [ i ] ∗ a [ k ] f[j]+a[i]*a[j]&lt;f[k]+a[i]*a[k] f[j]+a[i]a[j]<f[k]+a[i]a[k]
f [ j ] − f [ k ] &lt; a [ i ] ∗ a [ k ] − a [ i ] ∗ a [ j ] f[j]-f[k]&lt;a[i]*a[k]-a[i]*a[j] f[j]f[k]<a[i]a[k]a[i]a[j]
f [ j ] − f [ k ] &lt; a [ i ] ∗ ( a [ k ] − a [ j ] ) f[j]-f[k]&lt;a[i]*(a[k]-a[j]) f[j]f[k]<a[i](a[k]a[j])
( f [ j ] − f [ k ] ) / ( a [ k ] − a [ j ] ) &lt; a [ i ] (f[j]-f[k])/(a[k]-a[j])&lt;a[i] (f[j]f[k])/(a[k]a[j])<a[i]
所以,任何形式我们都可以转化成斜率式,以便二分。
下面是我从我看的第一个博客(%%%)中copy下来的:
在这里插入图片描述
bzoj1010[HNOI2008]完具装箱
bzoj1096[ZJOI2007]仓库建设
bzoj1597[USACO2008 Mar]仓库购买
bzoj1911[APIO2010]特别行动队
bzoj3156 防御准备
bzoj3675[Apio2014]序列分割
待更

2019.8.4 update

凸优化

凸优化是在有分段限制的情况下的一种优化方式。
对于每一次的选取加一个 K K K的代价。
显然, K K K越大分成的组数越小,反之越大。
所以,我们可以二分 K K K,最后答案便是 f [ n ] − m ∗ K f[n]-m*K f[n]mK
例题:
4475. 【GDOI2016模拟4.25】征途
4476. 羊羊列队
。。。

在转移的时候可以优化:
在这里插入图片描述

<think>我们正在讨论动态规划中的斜率优化技术。斜率优化主要用于优化形如 $dp[i] = \min_{j < i}\{ dp[j] + f(i, j) \}$ 的转移方程,其中 $f(i, j)$ 可以分解为关于 $i$ 和 $j$ 的函数的组合。斜率优化的核心思想是将转移方程转化为直线方程,利用决策点的斜率单调性,通过维护一个凸包(上凸包或下凸包)来快速找到最优决策点。 根据引用内容,斜率优化通常适用于以下情况: 1. 转移方程可以写成:$dp[i] = \min_{j < i} \{ dp[j] + A(i) \cdot B(j) + C(i) + D(j) \}$,其中 $A(i)$ 和 $B(j)$ 是分别只与 $i$ 和 $j$ 有关的函数。 2. 通过代数变形,将转移方程转化为:$dp[j] + D(j) = -A(i) \cdot B(j) + (dp[i] - C(i))$。这样,我们可以将每个决策点 $j$ 看作二维平面上的一条斜率为 $-A(i)$ 的直线,截距为 $dp[i]-C(i)$,而每个决策点 $j$ 对应于点 $(B(j), dp[j]+D(j))$。 3. 当 $A(i)$ 具有单调性(例如递增)时,我们可以利用单调队列维护凸包;当不具有单调性时,可能需要二分查找。 具体步骤: 1. **变形方程**:将转移方程变形,使得只与 $j$ 相关的项($dp[j]+D(j)$ 和 $B(j)$)作为点的坐标,而只与 $i$ 相关的项($A(i)$)作为斜率。 2. **维护凸包**:根据斜率的变化趋势(递增或递减)维护一个凸包(上凸或下凸)。通常,如果斜率是递增的,则维护下凸包(最小值);如果斜率是递减的,则维护上凸包(最大值)。但具体问题需要具体分析。 3. **寻找最优决策点**:在凸包上,最优决策点满足它前后的点所连成的直线的斜率与当前斜率 $k_i$ 满足一定的关系(例如,对于下凸包,最优决策点满足:前一个点与当前点的斜率小于 $k_i$,而当前点与后一个点的斜率大于等于 $k_i$)。 4. **加入新决策点**:在计算完当前状态 $dp[i]$ 后,将点 $(B(i), dp[i]+D(i))$ 加入凸包,同时维护凸包性质(删除破坏凸性的点)。 引用[1]中给出了一个例子:凸包上的斜率呈单增态,那么最优决策点就是第一个斜率大于等于 $k_0$ 的线段的左端点。由于斜率单调递增,可以使用二分查找。 引用[4]提到,当右边是一个递增的变量(即斜率单调递增)时,会选用斜率递增的单调队列(上凸包或下凸包,具体问题具体分析)。在引用[4]的图中,斜率递增,因此维护的是上凸包?但注意:通常下凸包对应最小值问题,上凸包对应最大值问题。但具体问题需要分析。 下面我们通过一个经典问题“任务安排”来具体说明(参考引用[5]的问题): 问题描述:有 $n$ 个任务,每个任务需要 $t_i$ 的时间,费用系数为 $c_i$。任务可以分批,每批任务有一个启动时间 $S$。每批任务连续完成。设第 $i$ 个任务完成的时间为 $T_i$,则其费用为 $T_i \times c_i$。总费用为所有任务费用之和。要求最小化总费用。 状态定义:设 $dp[i]$ 表示完成前 $i$ 个任务的最小费用。 转移方程:考虑将 $j+1$ 到 $i$ 的任务分成一批,则: $$ dp[i] = \min_{0 \leq j < i} \left\{ dp[j] + \text{sumT}_i \times (\text{sumC}_i - \text{sumC}_j) + S \times (\text{sumC}_n - \text{sumC}_j) \right\} $$ 其中,$\text{sumT}_i = \sum_{k=1}^i t_k, \text{sumC}_i = \sum_{k=1}^i c_k$。 变形:将只与 $j$ 相关的项提出来: $$ dp[i] = \min_{j} \left\{ dp[j] - (S + \text{sumT}_i) \times \text{sumC}_j \right\} + \text{sumT}_i \times \text{sumC}_i + S \times \text{sumC}_n $$ 令 $A(i) = S + \text{sumT}_i$,$B(j) = \text{sumC}_j$,$C(i) = \text{sumT}_i \times \text{sumC}_i + S \times \text{sumC}_n$,$D(j) = 0$,则: $$ dp[i] = \min_{j} \left\{ dp[j] - A(i) \cdot B(j) \right\} + C(i) $$ 移项: $$ dp[j] = A(i) \cdot B(j) + (dp[i] - C(i)) $$ 这里,$A(i)$ 是递增的(因为 $\text{sumT}_i$ 递增),$B(j)$ 也是递增的($\text{sumC}_j$ 递增)。 因此,每个决策点 $j$ 对应于点 $(B(j), dp[j])$。我们要求的是斜率为 $A(i)$ 的直线过某个决策点 $j$ 时的截距($dp[i]-C(i)$)最小。所以,我们维护一个下凸包(因为要求最小值,且斜率 $A(i)$ 递增)。 维护凸包步骤: 1. 在队列中,相邻点之间的斜率递增(下凸包)。 2. 当计算 $dp[i]$ 时,因为斜率 $A(i)$ 递增,所以队首的决策点可能不再是最优(因为后面的点可能更优),我们需要检查队首两个点之间的斜率是否小于 $A(i)$,如果小于,则队首点不是最优,弹出队首,直到队首两点斜率大于等于 $A(i)$。此时队首点即为最优决策点。 3. 计算 $dp[i]$。 4. 将点 $(B(i), dp[i])$ 加入队尾。在加入之前,需要检查队尾三个点是否保持凸性(即队尾三个点中,中间点是否在下面,如果不是则弹出队尾点,直到满足凸性)。 具体实现: - 使用双端队列(deque)存储决策点的下标(或者直接存储坐标)。 - 队列中存储的决策点对应凸包上的点。 伪代码: ```python 初始化:dp[0] = 0, 队列q,将0加入队列 for i in range(1, n+1): # 从队首弹出斜率小于当前斜率A(i)的点(因为斜率递增,所以队首的点可能不再是最优) while len(q) >= 2 and slope(q[0], q[1]) <= A(i): pop_front(q) j = q.front() # 最优决策点 dp[i] = dp[j] - A(i)*B(j) + C(i) # 将点i加入队列,维护凸包 while len(q) >= 2 and slope(q[-1], i) <= slope(q[-2], q[-1]): pop_back(q) push_back(q, i) ``` 其中,`slope(j, k)` 表示点j和点k之间的斜率:$(dp[k]-dp[j]) / (B(k)-B(j))$。 注意:这里假设 $B(k) \neq B(j)$,且 $B$ 是递增的,所以分母不为0。 通过以上步骤,我们可以在 $O(n)$ 的时间复杂度内完成动态规划。 总结:斜率优化动态规划的关键在于将转移方程变形为直线方程,利用决策点的凸性,通过维护凸包来快速寻找最优决策点。 §§相关问题§§ 1. 斜率优化动态规划适用于哪些类型的转移方程? 2. 如何判断一个动态规划问题是否可以使用斜率优化? 3. 斜率优化中,如何维护凸包? 4. 斜率优化中,当斜率不单调时,如何寻找最优决策点? 5. 能否举例说明一个斜率优化动态规划的具体问题?</think>### 动态规划中的斜率优化技术 斜率优化是动态规划的高级优化技术,用于**寻找最优决策点**并降低时间复杂度。其核心思想是将状态转移方程转化为几何形式(直线斜率),通过维护**决策点的凸包**(上凸包或下凸包)快速定位最优解。以下是关键技术要点: --- #### 一、斜率优化的适用条件 1. **状态转移方程形式** 需满足:$dp[i] = \min_{j < i} \\{ dp[j] + f(i, j) \\}$ 其中 $f(i, j)$ 可拆分为:$f(i, j) = A(i) \cdot B(j) + C(i) + D(j)$ ($A(i), B(j)$ 分别为 $i$ 和 $j$ 的函数) 2. **斜率单调性** - 若 $A(i)$ 单调递增/递减,可用单调队列维护凸包($O(n)$) - 若 $A(i)$ 无单调性,需二分查找凸包($O(n \log n)$) --- #### 二、实现步骤(以最小化问题为例) 1. **变形状态转移方程** 将方程改写为: $$ \underbrace{dp[j] + D(j)}_{Y_j} = \underbrace{-A(i)}_{斜率} \cdot \underbrace{B(j)}_{X_j} + \underbrace{(dp[i] - C(i))}_{截距} $$ 此时每个决策点 $j$ 对应平面点 $(X_j, Y_j) = (B(j), dp[j] + D(j))$。 2. **维护凸包** - **下凸包**(最小值问题):相邻点斜率递增($\frac{Y_{k+1}-Y_k}{X_{k+1}-X_k} \geq \frac{Y_k-Y_{k-1}}{X_k-X_{k-1}}$) - **上凸包**(最大值问题):相邻点斜率递减 *示例凸包维护(引用[4]):* > 斜率递增的单调队列对应下凸包,通过删除队尾破坏凸性的点保证斜率单调性[^4]。 3. **寻找最优决策点** - **单调队列法**($A(i)$ 递增时): 1. 删除队首斜率 $\leq A(i)$ 的点(因其不会更优) 2. 队首即最优决策点 $j^*$ *示例(引用[1]):* > 当凸包斜率单增时,找到第一个满足 $\frac{Y_j-Y_{j-1}}{X_j-X_{j-1}} \geq A(i)$ 的点 $j$,其左端点即最优决策点[^1]。 - **二分法**($A(i)$ 无单调性): 在凸包上二分查找满足 $\frac{Y_j-Y_{j-1}}{X_j-X_{j-1}} \geq A(i)$ 的最小 $j$。 4. **更新状态与决策点** 计算 $dp[i] = dp[j^*] + A(i) \cdot B(j^*) + C(i)$,并将点 $(B(i), dp[i] + D(i))$ 加入凸包。 --- #### 三、经典问题示例 **任务安排问题**(引用[5]): - **状态转移**:$dp[i] = \min_{j < i} \\{ dp[j] + (sumT_i + S) \cdot (sumC_i - sumC_j) \\}$ - **斜率优化**: - $A(i) = sumT_i + S$, $B(j) = sumC_j$ - 维护点 $(sumC_j, dp[j])$ 的下凸包 - 单调队列维护最优 $j$(因 $sumT_i$ 递增) *优化效果*:时间复杂度从 $O(n^3)$ 降至 $O(n)$[^5]。 --- #### 四、注意事项 1. **避免除零错误**:若 $X_j$ 可能相等,需特判(如直接比较 $Y_j$)。 2. **凸包方向**:最小值问题用下凸包,最大值问题用上凸包。 3. **决策单调性**:若 $B(j)$ 非单调,需使用平衡树/CDQ分治维护凸包。 > 核心优势:将动态规划中枚举决策的复杂度从 $O(n)$ 降至 $O(1)$ 或 $O(\log n)$,适用于大规模问题[^2][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值