分治优化 dp

· · 算法·理论

感觉这个套路有点强,所以记录一下。

写的可能有点问题 (轻喷),欢迎各位大佬指出。

写得可能不是很完善,欢迎各位大佬补充。

分治思想就不做介绍了,本文不讨论分治做转移分层的决策单调性。

适用情况

对于转移形如 f(i) = \min\limits_{j < i} (f(j) + w(j, i)) 或者 f(i) = \min\limits_{j < i} (w(j, i)) 的 dp。

如果贡献函数 w 具有可合并性,即 w(l, r) 可以通过 w(l, k)w(k + 1, r), k \in [l, r) 合并得到那么它可能可以使用分治来优化,值得注意的是转移不分层不一定可以优化,一般而言可以将 \mathcal{O}(N ^ 2) 优化到 \mathcal{O}(N m \log N)\mathcal{O}(m) 是合并贡献的复杂度。

过程

我们考虑设计函数 \text{solve}(l, r) 表示处理区间 [l, r] 的转移:

过程看起来很简单,但是这个思想带来的优势是非常大的。

首先我们不需要动态的加入元素,也就是说我们可以将处理了的元素所有信息静态维护,不需要管另外一边对已处理元素贡献的影响。

还有 dp 树是可能的 dp 树中深度最小的,这就让一些可能的暴力复杂度正确。

例题

CF 1442 D

题目描述

N(1 \le N \le 3000) 个物品组,第 i 个物品组有 t_i(1 \le \sum t_i \le 10 ^ 6) 个元素,第 i 个物品组第 j 个物品的贡献是 a_{i, j}(1 \le a_{i, j} \le 10 ^ 8),同时满足 \forall j \in [1, t_i - 1], a_{i, j} \le a_{i, j + 1},现在可以选择 k(1 \le k \le 3000) 个物品,每个物品只能选一次,且选择第 i 组第 j 个物品之前必须将第 i1j - 1 的物品选择完,要求最大化贡献。

思路

一个简单的贪心是我每次肯定是选择拿完一组物品是最优秀的。

因此根据这个贪心,我们可以将物品组分成三类,第一类是拿完了的,第二类是拿了部分,第三类没有拿元素,很显然第二类物品最多只有一组。

因此我们可以枚举第二类物品是哪一组,剩下的组通过零一背包求出最大贡献。

这样子的复杂度是 \mathcal{O}(N ^ 2 k) 的。

怎么优化呢,我们考虑这个算法的瓶颈在哪里,我们每次需要重新求出来 N - 1 个物品的背包,其实是没有必要的。

我们考虑分治,尽可能利用信息。考虑现在处理了区间 [l, r] 的背包,然后直接回溯剩给上一层,对于 l = r 的节点单独跑一个零一背包,这样子我们只需要做总共 \mathcal{O}(N) 次合并两个背包,复杂度降到了 \mathcal{O}(Nk \log N)

CF 1175 G

题面描述

给定一个数组 a_1​,a_2​,…,a_n​,你需要将其分成 k 个子段(每个元素恰好属于一个子段)。

一个子段 a_l​,a_{l+1}​,…,a_r​ 的权值定义为 (r−l+1)⋅l≤i≤r\max​(a_i​)。一个划分的权值是所有子段权值之和。

请你找到权值最小的划分方案。

思路

f(i, k) 表示前 i 个数划分 k 段的最小贡献。

f(i, k) = \min(f(j, k - 1) + (i - j) \times \max\limits_{p = j + 1} ^ i a_p)

然后考虑分治处理,考虑现在用 [l, mid] 转移 [mid + 1, r]

我们设 mx_{1, j} = \max\limits_{i = j + 1} ^ {mid} a_i, mx_{2, j} = \max\limits_{i = mid + 1} ^ j a_i

接下来我们分别考虑 mx_{1, j} \ge mx_{2, i}mx_{1, j} < mx_{2, i} 的情况。

先考虑 mx_{1, j} \ge mx_{2, i} 的情况,f(i, k) = \min(f(j, k - 1) - j mx_{1, j} + i mx_{1, j})我们从大到小枚举 i,从小到大枚举 j,然后依次加入线段就可以了,李超线段树查询一下。

对于 mx_{1, j} < mx_{2, i} 的情况,f(i, k) = \min(f(j, k - 1) - jmx_{2, i}) + imx_{2, i},同样的将 i 从小到大来,然后 j 从大到小来,同样用李超线段树维护。

总结

分治可以将转移转换为静态的从已有的元素转移给新的元素,而不需要动态插入,在贡献可合并的情况,复杂度是比较优秀的。

练习题