dp 多维状态的分步优化

面对一个多维 \(\text{dp}\) 问题,根据维度之间联系的紧密程度,我们可以选择

  1. 维度之间紧密相关,只能直接枚举

  2. 维度之间完全无关,只是贡献通过某种形式相加,可以割裂为两个dp处理

  3. 介于 \(1,2\) 之间,不能割裂计算,但是可以将转移过程割裂为若干步来优化

e.g.1: 选区间1

问题描述

对于所有二元组 \(a,b, (a,b\in[1,n]\cap \mathbb{Z}, a\leq b)\),给出了其权值 \(w_{a,b}\)

现在求一个元组序列 \(A=(a_1,b_1),(a_2,b_2),\cdots\),满足 \(a_1<a_2,b_2<b_1\)

定义 \(\displaystyle w_A=\sum w_{a_i,b_i}+\sum w_{a_{i-1},a_i}+\sum w_{b_{i-1},b_i}\) ,最大化\(w_A\)

\(\text{dp}\)

\(dp_{a,b}\)表示最后一个元组为\(a,b\)时的最大权值,状态数为\(O(n^2)\)

直接转移复杂度为\(O(n^2)\),总复杂度为\(O(n^4)\)

分布转移

两维的状态决定了权值,因此不可以割裂

但是两个维度在转移时并没有必然联系,因为只涉及 \(a_{i}\)\(a_{i+1}\)\(b_i\)\(b_{i+1}\) 的关系

因此可以先转移\(a\)这一维,然后再转移\(b\)这一维

具体的,转移可以描述如下

  1. \(dp_{a,b}+w_{a,c}\rightarrow f_{c,b}\)

  2. \(f_{c,b}+w_{d,b}+w_{c,d}\rightarrow dp_{c,d}\)

优化后转移复杂度为 \(O(n)\),状态数虽然加倍但是不影响量级

总复杂度为 \(O(n^3)\)


e.g.2: 选区间2

问题描述

对于所有二元组 \(a,b, (a,b\in[1,n]\cap \mathbb{Z}, a\leq b)\),给出了其权值 \(w_{a,b}\)

现在求一个元组序列 \(A=(a_1,b_1),(a_2,b_2),\cdots\),满足 \(a_1<a_2,b_2<b_1\)
定义 \(\displaystyle w_A=\sum w_{a_{i-1},a_i}+\sum w_{b_{i-1},b_i}\) ,最大化 \(w_A\)

\(\text{dp}\)

\(dp_{a,b}\) 表示最后一个元组为 \(a,b\) 时的最大权值,状态数为 \(O(n^2)\)

直接转移复杂度为 \(O(n^2)\),总复杂度为 \(O(n^4)\)

分布转移

容易发现这个问题同样适用选区间 \(1\) 的优化

割裂

容易发现权值由 \(a_i,a_{i+1}\)\(b_i,b_{i+1}\) 决定,不需要知道过程中每一个 \(a_i,b_i\) 的组,只需要知道数量

\(a,b\) 有单调性,所以关于 \(a\leq b\) 的限制只需要最后满足即可

\(f_{i,j}\) 表示已经枚举了 \(i\) 个元素,最后一个 \(a_i=j\) 时的最大值

同理,令 \(g_{i,j}\) 已经枚举了 \(i\) 个元素,最后一个 \(b_i=j\) 时的最大值

状态数为 \(O(n^2)\),转移复杂度为 \(O(n)\),最后可以在 \(O(n^2)\) 时间内合并 \(f,g\) 的贡献

总复杂度为 \(O(n^3)\)


e.g.3: 足球

Source: COCI2012/2013 Contest#5 F

问题描述

\(2n\) 个人踢球,两队各 \(n\) 个人,一开始球在 \(A\)\(1\)

每秒钟,按照一定概率球可能会被某一些对手抢走,或者传给某一些队友,或者射门

射门只有一定概率 \(p_i\) 射中,每次射门之后球会到对方 \(1\) 号队员

\(T\) 秒后比分为 \(a,b\) 的概率,如果一队得分达到 \(r\),视作胜利,比赛结束

为了便于分析,\(O(n)=O(T)\)

\(\text{dp}\)

记录时间、比分、球的位置,状态数为 \(O(Tnr^2)=O(n^2r^2)\)

转移枚举的情况不超过 \(2n\),转移复杂度为 \(O(n)\),总复杂度为 \(O(n^3r^2)\)

割裂

在记录比分的同时记录球的位置并没有意义,因为实际上关键事件实际上就是射门,每次射门之后球所在位置情况是\(O(1)\)

那么新的 \(\text{dp}\)球的位置比分情况 割裂开来

  1. 处理球的位置,令 \(f_{i,j,k}\) 表示一开始球在 \(i\)\(1\) 号时,第一次射门是在 \(j\) 时刻,由球员 \(k\) 射门(\(k\) 可以是两队中任何一个)

状态数为 \(O(Tn)\),转移为 \(O(n)\),这一部分复杂度为 \(O(n^3)\)

  1. 比分情况

\(g_{i,j,a,b}\) 表示 \(i\) 时刻,球在 \(j\)\(1\) 号,当前比分为 \(a:b\) 的概率

状态数为 \(O(Tr^2)\),转移为 \(O(T)\),总复杂度为 \(O(T^2r^2)\)


e.g.4: 异或

Source: CSP-S 2020 初赛完善程序2

给定长度为 \(n\) 的序列 \(a_i\in[0,m),m=2^{16}\)\(n\leq 10^{5}\)

\(w(x)=\text{pop_count}(x)+x\),求一个子序列 \(b_i\)

最大化 \(\sum w(b_i\oplus b_{i+1})\)

\(\text{dp}\)

\(dp_{x}\) 表示子序列最后一个元素为 \(x\) 时的答案,状态数为 \(O(m)\)

对于每个数 \(a_i\),枚举前驱状态进行转移,复杂度为 \(O(m)\)

总复杂度为 \(O(nm)\)

分布转移

这道题看似是一个 \(1\)\(dp\),但是实际上权值是分位处理的

我们不如先看一个类似的选区间问题的变体

对于所有二元组 \(a,b, (a,b\in[1,n]\cap \mathbb{Z})\),给出了其权值 \(w_{a,b}\)

给定了一个元组序列 \(A=(a_1,b_1),(a_2,b_2),\cdots\)

现在要选出 \(A\) 的一个子序列 \(B\),定义 \(\displaystyle w_B=\sum w_{a_{i-1},a_i}+\sum w_{b_{i-1},b_i}\) ,最大化 \(w_B\)

实际上这两个问题是完全相同的,但是由于限制了 \(a_i,b_i\) 为子序列,导致分布显得比较奇怪

分布转移的过程中,转移状态应该是这样的

\((a_1,b_1)\rightarrow (a_2, b_1) \rightarrow (a_2,b_2)\)

容易发现这个过渡状态\((a_2,b_1)\)实际上并不是一个存在的元组,并不满足匹配关系

那么我们如何得到这个过渡状态呢?

  1. 我们手里有\((a_1,b_1)\)的dp值\(dp_{a,b}\)

  2. 我们并不知道\(a_2\),于是需要向所有可能的\(a_2\)转移,得到 \(f_{c,b}\)\(\forall c,dp_{a_1,b_1}+w(b_1,c)\rightarrow f_{c,b_1}\)

  3. 当拿到 \(a_2,b_2\) 时,此时我们已经知道了 \(a_2\),但是不知道 \(b_1\),因此需要从所有 \(b_1\) 中得到 \(dp_{a_2,b_2}\) 的值

而为了分布转移,我们在中间过程中并不需要

\(\forall c,f_{a_2,c}+w(c,b_2)\rightarrow dp_{a_2,b_2}\)

分析会发现,\(dp_{a,b}\) 反而在这个过程中只是一个过渡值,并不需要开数组记录

于是就得到了完善程序的 \(\text{dp}\)


e.g.5: 树

Source: ZJOI 2022 Day1 T1

给你两棵树,然后要求第一棵树里每个点向着自己编号小的点连边,第二个是自己大的点连边。

称一种方案是好的当且仅当对于第一棵树的一个非叶子节点,它是第二棵树的叶子节点;对于第二棵树的非叶子节点,它是第一棵树的叶子节点。

\(n\le 500\)

\(\text{dp}\)

\(dp_{i,j,k,l}\) 表示第一棵树前 \(i\) 个点有 \(j\) 个非叶子,其中有 \(k\) 个还没有儿子,第二棵树有 \(l\) 个点没有确定父亲(父亲编号大于 \(i\))的方案数。

转移时若第 \(i+1\) 个点在第一棵树中是叶子,则需要枚举 \(o\) 表示其在第二棵树中有 \(o\) 个儿子。复杂度 \(O(n^5)\)

分布转移

我们考虑一个点是接在第一棵树上的情况,对于一个非叶子节点,给它加入一个儿子后它有可能还会在后面再加入儿子,也有可能在后面不会加入儿子了。

我们可以在插入一个节点时枚举该节点是否是它父亲的最后一个儿子,得到新的 \(\text{dp}\) 状态 \(dp_{i,j,l}\) 表示前 \(i\) 个点中有 \(k\) 个点在第一棵树中有编号在 \([i+1,n]\) 之间的儿子,状态数 \(O(n^3)\) ,转移 \(O(n)\) ,总复杂度 \(O(n^4)\)

对于 \(i\) 号点在第二棵树中不是叶子的情况,在转移时需要枚举它的儿子数量,发现这样比较麻烦,我们可以考虑省去枚举的过程,在插入一个节点时直接把儿子集合划分好,到时直接选一个儿子集合接上就可以了。

也就是令 \(dp_{i,k,l}\) 表示前 \(i\) 个点中有 \(k\) 个点在第一棵树中有编号在 \([i+1,n]\) 之间的儿子,第二棵树中还未确定父亲的点被划分成了 \(l\) 个集合,状态数 \(O(n^3)\),转移 \(O(1)\) ,总时间复杂度 \(O(n^3)\)


e.g.6: 火之神神乐

一个长为 \(n\) 的数轴,每个位置有 \(p_i\) 个概率产生一个向右走的人,\(1-p_i\) 的概率产生向左走的人,两个人相遇后有 \(P\) 的概率向右走的人或下来,\(1-P\) 的概率向左走的人活下来,活下来的人会继续向前走,问最终有 \(a\) 个向右走的人活下来,\(b\) 个向左走的人活下来的概率是多少。

\(n\le 5000\)

\(\text{dp}\)

把往正方向跑的视作右箭头 \(→\),把往负方向跑的视作左箭头 \(←\),两者碰到就有 \(P\) 的概率左箭头寄了,每个位置有 \(p_i\) 概率是\(→\)

\(f_{i,j,k}\) 表示 \(\text{DP}\)\(i\),有 \(j\) 个左箭头向左畅通无阻,这 \(j\) 个的右边还有 \(k\) 个右箭头在往右走的概率,具体转移就是看这个位置是 \(→\),还是 \(←\),如果是 \(→\),直接给 \(k\) 加一,否则枚举消掉多少个 \(→\) 即可,时间复杂度 \(O(n^3)\)

分布转移

我们换一种思路,枚举一个箭头之间的间隔 \(i\in[0,n]\),表示前 \(i\) 个箭头中 \(→\) 都寄了,\(←\) 都出去了,恰好有 \(b\) 个,右边大同小异,看一边就可以了。

发现还是要设 \(f_{i,j,k}\) 表示 \(\text{DP}\)\(i\),有 \(j\) 个左箭头向左畅通无阻,这 \(j\) 个的右边还有 \(k\) 个右箭头在往右走的概率。

不过我们最终只需要 \(k=0\) 的状态,而 \(j\)\(k\) 相对独立,考虑将它们合并成一维,我们不在插入 \(←\) 时枚举它消掉了多少个 \(→\) ,而是在插入 \(→\) 时枚举它会消掉多少个 \(←\)

\(f_{i,j}\) 表示前 \(i\) 个箭头中 \(→\) 都寄了,\(←\) 都出去了,后面还需要出去 \(j\)\(←\) 的概率,初值设 \(f_{0,b}=1\) .

类似地设 \(g_{i,j}\) 表示后 \(i\) 个箭头中 \(←\) 都寄了,\(→\) 都出去了,后面还需要出去 \(j\)\(→\) 的概率,初值设 \(g_{0,a}=1\) ,最终答案为 \(\sum_{i=1}^{n-1}f_{i,0}\times g_{i+1,0}\) ,时间复杂度 \(O(n^2)\)

posted @ 2022-07-05 18:51  一粒夸克  阅读(168)  评论(0编辑  收藏  举报