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

线头 DP

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

原文地址:https://www.cnblogs.com/nealchen/p/10162980.html