【1 月小记】Part 5: DP 优化

DP 优化

持续更新中……

前缀和优化

P2513 [HAOI2009] 逆序对数列

这题不加优化也能过,难崩

考虑这个状态定义是怎么来的。倘若尝试将 \(n\) 排列的具体顺序融入状态定义会发现需要维护一个状压状的维度,数据范围太大,肯定不可做;然后注意到,因为你每次往排列里放的都是一个比原来所有数都要大的数,所以状态决策跟 \(n\) 排列的具体顺序完全无关。于是不妨定义 \(f_{i, j}\) 表示 \(i\) 的排列中,存在 \(j\) 个逆序对的方案总数。

考虑 \(i\) 的排列是 \(i - 1\) 的排列转移而来的情况。因为后加的那个数字比先前的数字都要大,所以原来的前缀和数目 \(p\) 满足以下关系式:

\[\begin{cases} j - p_{\min} = i - 1 \Rightarrow p_{\min} = j - i + 1 \\ p_{\max} = j \end{cases} \]

确定了 p 的范围之后,就有

\[f_{i, j} = \sum_{p = \max(0, j - i + 1)}^{j} f_{i - 1, p} \]

现在的时间复杂度是 \(O(n^2 * k)\) 的,考虑到状态转移的复杂度,我们可以对其进行优化。如何优化?我们可以使用前缀和来加速状态转移。

不妨定义 \(s = \sum_{p = 0}^{j} f_{i - 1, p}\)。因为我们在对 \(f_{i, j}\) 实施转移时,求的总是一段既有区间的和;每次 \(i\) 指针往前移动时,既有区间只会增加 \(f_{i - 1, j}\) 这一个值,因此只需在每次循环中将 \(f_{i - 1, j}\) 累加进 \(s\) 中即可。

注意特判 \(\max(0, j - i + 1) = j - i + 1\)\(j - i + 1 > 0\) 的情况,此时需要将 \(s\) 减去 \(f_{i - 1, j - i + 1}\) 这一部分。

记得取模。

CF383E Vowels

高维前缀和优化 DP,也称作 SOS-DP。

注意到字母种类非常少,我们可以使用状压,把每个字母压到一个二进制位上。

如果直接求交集不太可做,因此考虑正难则反,求补集。

考虑定义 \(f_{st}\) 表示以集合 \(st\) 为元音字母集合的补集时,不合法的单词数目。

一个单词是不合法的,说明所有的元音字母都不包含在里面,进而说明它包含于元音字母集合的补集。

所以我们只需要在元音字母集合的补集上跑高维前缀和枚举子集即可。

单调数据结构优化

P2569 [SCOI2010] 股票交易

对于每支股票,可以选择不动、买入或卖出,有点像一个含有三重决策的背包状问题。

不妨设 \(f_{i, j}\) 表示第 \(i\) 天,手里有 \(j\) 股股票时能赚到的最多的钱。

  1. 不动

\[f_{i, j} = f_{i - 1, j} \]

  1. 买入

\[f_{i, j} = \max_{k = j - as_i}^{j}(f_{i - w - 1, k} - ap_i (j - k)) \]

  1. 卖出

\[f_{i, j} = \max_{k = j}^{j + bs_i}(f_{i - w - 1, k} + bp_i (k - j)) \]

以上三种情况取 \(\max\)。注意边界问题!

由于买卖股票过程中可能会亏钱,即 \(f\) 数组值存在负数,所以最开始一定要赋一个极小值(不是 \(0\))。边界显然是 \(f_{0,0} = 0\)

考虑如何对它进行优化。显然转移 1 无法优化。

注意到转移 2 意味着每一个 \(j\) 指针的前面都挂着一个长度为 \(as_i\) 的窗口。把状态转移方程展开,因为 \(-ap_i \cdot j\) 是个定值,可以挪到 \(\max\) 的外面,所以我们的目标就是要找到这个窗口里最大的 \(f_{i - w - 1, k} + ap_i \cdot k\)。写一个滑动窗口即可。

转移 3 的优化方式和转移 2 基本相同,不过它是 \(j\) 指针的后面挂着长度为 \(bs_i\) 的窗口。这样的话,我们就需要倒序遍历 \(j\) 的值。其他没什么不同。

P6563 [SBCOI2020] 一直在你身旁

说点闲话吧。

这题被 yyy 珍爱有加,在 24N2 公众号上曾被当作文章的一部分来投稿。早些时候 yyy 曾与我推荐过这道题,称“会了这道题就掌握了区间 DP 的精髓”。

题目背景源于动漫《Clannad》。那是我前年 12 月份看的一部动漫了,情节如此打动人心,给了我很深的印象。似乎当时发了高烧,在家请了近两周的假没去上学,把两季连着追完了。记得当时给我看哭了三回。我想我一个男的怎么会因为这点小事就哭鼻子?后来啊,后来又连着发生了许多这样的小事,又连着哭了好多回,这才明白我原来是这么软弱的一个人啊。

那时我的生活里还没有 OI 的参与,我的一切理性都被奉献于平面几何的研究中。那个寒冬,也是我平几研究水平的一个高峰时期。我自尊心很强,很怕自己的实力被别人践踏,怕自己的平几水平撵不上 zzj(当然他现在也是我的高中同学了),于是没日没夜的做平几。很单调,不是吗?幸好有《Clannad》,看到朋也的担当,渚的善良,汐的纯真,又给我那时的生活填上了一抹独特的色彩啊。

说到这部动漫,又让我想起她了。她和我一样喜欢 ACG 文化(甚至我很大程度上就是被她带入坑的),分享欲很强,总是和我推荐几部她觉得不错的动漫,其中就包括《Clannad》。

可能是我的性格使然吧,我比较偏爱智代这个人物。那时实在很天真啊,总以为梦想中的爱恋总能成真,于是试图将那份都算不上爱恋的爱恋映射到朋也和智代的故事中,似乎觉得这是挺浪漫的一件事。每一天都幻想着我能和她像朋也和智代一样在雪地里重逢、冲刷过去再到敞开心扉。那一月我持续在用朋也作为我的微信头像。果然她给我留下了很多美好的回忆呀。

12 月份的尾声在科技馆奏响,紧接着是 1 月份的乐章。她不知怎的就逃出了我的生活。如果把我的性格比喻成一种颜色的话,那它应当原本是鲜红的;遇到她之后,一抹深邃暗淡的蓝抹了上去,而且无法褪色。我受她影响很大。我原本不是这样的。我理应变得更加理性,用我喜欢的方式思考这个世界。但我做不到。我总喜欢往以前想,回头看,看那些记忆中的人的背影渐渐消散。

好了好了,还是回归正题吧。

定义 \(f_{l, r}\) 表示已知电线长度在区间 \([l, r]\) 内时,查明电线长度所需的最小花费。

想要查明区间 \([l, r]\) 的状况,必须在其中间挑选一根电线购买,即

\[f_{l, r} = \min_{k = l}^{r}(\max(f_{l, k}, f_{k + 1, r}) + a_k) \]

断开的两个子区间要取 \(\max\) 的原因在于,我们求的实际上是在最劣的情况下,最少要支出的开销(因为题目中问的是“至少”)。

现在你得到了 20 pts,接下来是优化。因为 \(\min\)\(\max\) 很麻烦,所以考虑把 \(\max\) 分讨掉。进而,需要找到刚好使得 \(f_{l, k} \geq f_{k + 1, r}\)\(k\)

发现若钦定 \(r\),则该分界点 \(k\)\(l\) 不降而不降。进而进一步分析确定分界点后的转移方程。

  1. \(f_{l, r} = \min_{l \leq k < r}(f_{l, k} + a_k)\)
    此时要想使得 \(f_{l, k}\) 取最小,必须发现它单调不减,且 \(a\) 数组也单调不减,进而最小值就是 \(k\) 取区间分界点的情况,两者都是最小的。

  2. \(f_{l, r} = \min_{l \leq k < r}(f_{k + 1, r} + a_k)\)
    可以认为 \(r\) 后面挂着一个长度为 \(r - k + 1\) 的窗口,要求维护最小的 \(f_{k + 1, r} + a_k\)。滑动窗口即可。

这道题我仍然没有理解透,需要回过头再看。是一道好题。

posted @ 2026-01-16 22:06  L-Coding  阅读(3)  评论(0)    收藏  举报