从 vim 一题看线头 DP 的扩展应用

线头 DP

我们知道,线头 DP 是我们搞定一类只和两侧元素有关的排列问题的利器。

我们在数轴上,将排列中相邻的两个数连线,这样得到一条折线。以边为单位,考察穿过这条边的折线段的影响,进行动态规划。

线头 DP 在更广泛的折线路径类问题上的应用

我们以「BalticOI 2013」Vim 为例分析线头 DP 的扩展应用。

既然我们知道,线头 DP 先将排列转为了折线,我们就可以拿它来解决更广泛的折线路径类问题。

在本题中,我们可以将光标的移动视为折线。注意到我们删除一个 $e$,一定是在该空格之后的那个位置进行操作 hx。因此所有的空格都需要两步,这样我们删去那些 $e$,问题转化为经过一些关键点(原左边为 $e$ 的位置)的最小步数了。

我们将 f 操作引发的折线叫作飞线,h 操作引发的折线叫作跳线。我们知道,折线路径一定是这样的:每次先通过若干次飞线连成的飞链降落在某点,然后往回跳到出发点右侧第一个关键点。

为了计算答案的清晰性,我们认为每次操作的步数都在起点计算。

从两个点之间的间隙将折线剪开,至多会剪断两条飞线和一条跳线。如果没有剪到飞线,那么说明整个折线已经结束了。我们知道飞线的降落地点只和其字符有关。

于是我们设 $f(i, j, k)$ 表示在第 $i$ 个字符右侧剪开折线,剪到的两条飞线中所属飞链起点较左的那条的字符为 $j$,所属飞链起点较右的那条的字符为 $k$ 时,在前 $i$ 个字符内计算的步数最小值。特别地,$k=\mbox{nil}$ 表示它只穿过了一条飞线。

根据这个我们进行分类讨论,设计动态规划。

由于最后一段飞链回跳后可以不必再引出飞线,所以要特殊考虑。我们在倒数第二段飞链结束的时候来计算答案。

扫描二维码关注公众号,回复: 4611068 查看本文章

我们利用序列自动机,求出第 $i$ 个点右边第一个字符 $j$ 的位置 $\mbox{next}_{ij}$ 和第 $i$ 个点右边第一个关键点 $\mbox{nextkey}_i$。那么,如果最后一条飞链在第 $i$ 个点降落,且最后一条飞链跳到了 $j$,设最后一个关键点位置为 $\mbox{lastkey}$,那么就要满足 $j\ge \mbox{lastkey}$,回跳的步数为 $j-\mbox{nextkey}_i$。

因此我们令 $d(i)$ 表示从第 $i$ 个点经过 $s$ 条飞线降落在 $t$,使 $t \ge \mbox{lastkey}$ 时最小的 $2s+t$,进行动态规划。

于是答案就是 $\min\big\{f(i, j, \mbox{nil})+d(\mbox{next}_{ij})-\mbox{nextkey}_i \big| 1 \le i \lt \mbox{lastkey}, j \in \{a, b, c, d, f, g, h, i, j\}\big\}$。

猜你喜欢

转载自www.cnblogs.com/nealchen/p/10162980.html