ymh dp选讲

ymh dp选讲

CF626F Group Projects

题意:

\(n\) 个人,每个人有一个权值 \(a_i\) ,现在需要把这些人分成若干个组,不区分组编号,每个组的代价为 \(a_{max}-a_{min}\) ,求所有组代价之和不超过 \(k\) 的方案数

其中 \(n\le 200,k\le1000,a_i\le 500\)

思路:

考虑到若把这些人排序,代价只与每组的第一个人和最后一个人相关,所以我们先把代价排序,设 \(dp_{i,j}\) 表示当前推到了第 \(i\) 个人,代价为 \(j\) 的方案数。那么对于第 \(i\) 个人,我们有 \(3\) 种选择:

  1. 自成一组,且该组只有 \(i\) 自身
  2. 自成一组,且该组不只有 \(i\) 自身
  3. 与前面任意一个组合并,且不当作结尾
  4. 与前面任意一个组合并,且当作该组结尾

我们考虑到当前方案数的计算还会与当前的组数相关,所以我们增设状态 \(dp_{i,j,k}\) 表示当前推到了第 \(i\) 个人,当前未结尾的组数为 \(j\) 组,当前总代价为 \(k\) 。所以上面 \(4\) 种情况的转移为 \(O(1)\) 复杂度

但是我们注意到当前若在每次遇到 \(2\) 情况时都在代价内减去 \(a_i\) ,会导致 \(k\) 最小值到达 \(-250*500\) , 导致枚举时时间复杂度变为 \(n\times n\times k\times V\) 。我们可以考虑把代价均摊到每一次伸长结尾节点时。具体来讲,新建一个差分数组,把代价的计算改为每次从 \(i\)\(i+1\) 加上 组数 \(\times\) 差分,从而使得每次代价只会存在加法。

AGC035D

题意:

有一个由 \(N(\le 18)\) 张牌组成的牌堆,每一张牌上都写有一个非负整数,自顶部开始,第 \(i\) 张牌上的数字为 \(a_i\)

\(Snuke\) 将重复以下操作,直至牌堆里只剩两张卡为止: 从牌堆里选择三张连续的卡,把三张卡中位于中间位置的卡吃掉, 把剩余的两张卡上的数字加上被吃掉的卡的数字后按照原来的顺序放回牌堆。

请找出最后剩下的两张牌上所写的数字之和最小是多少。

思路:

首先由题意可知,每次删掉一张牌,这张牌的代价都会被摊到左边和右边两个区间内,可以考虑区间 dp。但是我们对于删牌操作时被删牌对于左右两边贡献代价的系数无法确定,且当我删掉一张牌 \(p\) 的时候,\([l,p-1]\)\([p+1,r]\) 会拼在一起,从而相互影响。

所以我们当前合并区间的时候无法考虑最先删除的节点,转变思路,因为一个区间中最后删除的数字对区间答案的贡献一定为 \(2\) 倍,所以可以枚举最后被删除的节点 \(p\)

\(dp_{l,r}\) 为区间 \([l+1,r-1]\) 中的数字已经被删除完毕,此时区间 \([l+1,r-1]\) 对答案的贡献值。但是我们发现这样设状态是无法转移的,因为我们在枚举最后被删除的节点 \(p\) 时,\(a_p\) 会被贡献到 \(a_l\)\(a_r\) 上,但是 \(a_l\) 仍然会再被继续贡献到更左边的节点值上, \(a_r\) 同理。

那么我们不妨在记录下来 \(a_l\)\(a_r\) 分别还会被贡献多少次,增设状态,设 \(dp_{l,r,x,y}\) 表示区间 \([l+1,r-1]\) 中的数字已经被删除完毕,此时区间 \([l+1,r-1]\) 对答案的贡献值,且 \(a_l\) 还会被合并 \(x\) 次,\(a_r\) 会被贡献 \(y\) 次。那么我们此时最后被合并的节点 \(p\) 就会被合并 \(x+y\) 次,即对答案贡献 \(x+y\) 次。

此时转移是简单的。

我们考虑时间复杂度,经过大致计算约为 \(n^3\times 2^n\) 的(\(2^n\) 是由于最多转移 \(n\) 层),但是我们的复杂度满足递推式

\[T(n)=2\sum_{i=0}^{n-1} T(i) \]

解出来是 \(T(n)=3^n\) 的,所以时间复杂度是 \(O(3^n)\)

AGC039E

题意

圆周上有 \(2N\) 个点等分圆周,给出这 \(2N\) 个点之间的连边,保证任意三条边不会交于一点,选出 \(N\) 条边,使得从任意一个端点出发,只经过选择的边(可以通过边之间的交点切换到另一条边上)而不经过圆周,能够遍历所有的点且不会经过环状路径,即连出的边是一棵树。

题解:

观察题意可得:

  1. 每个点有且只有一条连边
  2. 若圆形内部出现一个 \(\times\) 号,则不可能有一条边能够同时穿过这个形成 \(\times\) 的两条边。

所以对于一个区间来说,该区间内肯定有至少一个点连向了该区间外,我们不妨把这样的边称为 外边

那么该区间内剩下的点只有两种选择:

  1. 选择一条经过这条 外边 的边
  2. 经过上面 1 操作形成的新边

与此同时,遇到环的问题我们首先考虑破环成链。并且我们只关心该区间内 外边 是从哪个点发出而不关心连向哪个点。所以我们设 \(dp_{l,r,k}\) 表示在区间 \(l\sim r\) 里的点只有 \(k\) 号点形成了一条 外边,枚举 \(1\) 号点连向哪个点,显然最终答案为 \(\sum_{i=2\text{且} E_{1,i}=1}^n dp_{2,n,i}\)

我们开始考虑转移,对于 \(dp_{l,r,k}\) 来说,\(l\sim k-1\) 的点要选出 \(q_1<q_2<\dots<q_t\) ,与右侧的 \(p_1>p_2>\dots>p_t\) 相连,但是我们无法具体钦定这 \(t\) 条边,所以我们优先考虑枚举最上面的那条边,此时我们的子问题就缩小为了 \(dp_{q_1+1,p_1-1,k}\) 。但是发现这种情况无法考虑到 \(i\sim q_1-1\) 的连边情况,经过上面的策略我们知道必须从这些点中至少选出一个与 \(q_1+1\sim k\) 的点相连,否则无法满足树的连通性。那我们考虑这部分点,发现如果出现了一个点 \(x\)\(i\sim q_1-1\) 中的点有连边,那么 \(q_1+1\sim x-1\) 中的点都无法再与 \(k\) 形成的 外边 相连。所以我们一定有一个分界线 \(x\),使得 \(q_1+1\sim x\) 的点都与 \(i\sim q_1-1\) 的点至少有一条连边,对于区间 \(k+1\sim r\) 也同理。

所以我们成功把 \(dp_{l,r,k}\) 这个大问题缩小到了 \(dp_{l,x,q_1}\)\(dp_{x+1,y-1,k}\)\(dp_{y,r,q_n}\)

时间复杂度为\(O(n^7)\)

通过这个题可以发现,我们对于一个区间 \(dp\) 来说,每个区间一定与其相邻的区间存在千丝万缕的关系,我们重要的就是找到这种关系,能够把这种关系在合并时天然消除或把这个关系记录到状态之中。

CF1149D

题意

一张 \(n\) 个点 \(m\) 条边的无向图,只有 \(a,b\) 两种边权,其中 \(a<b\)

对于每个 \(i\),求图中所有的最小生成树中,从 \(1\)\(i\) 距离的最小值。

其中 \(n\le 70\)\(m\le 200\)

思路

\(kruscal\) 的过程可知,边权为 \(a\) 的任何一条边总是能够出现在该图的最小生成树中,所以我们不妨先把边权为 \(a\) 的联通块全部找出建图。那么对于一个联通块内的点来说,到 \(1\) 的最短距离一定是若干个边权为 \(a\) 和一个边权为 \(b\) 交替出现的。

但是我们无法保证经过最少的边权为 \(b\) 的边形成的答案一定最优,因为我们若考虑从一个联通块出发,走若干条 \(b\) 边再回到该联通块,这样形成的答案有可能回比直接经过该联通块内的 \(a\) 边形成的答案要小。(例如该联通块是一条无限长的链,从链的一个端点出发走到另一端点的答案可能大于从该端点经过 \(b\) 边走向另一联通块,再从该联通块经过 \(b\) 边走到另一端点)

但是这样形成的答案显然是不合法的,因为最小生成树中不可能存在环,即该题指定的最优路程中不可能出现重复经过一个联通块的情况,

我们开始设计 \(dp\) 状态,设 \(dp_{S,i}\) 表示 \(S\) 内的联通块已经被经过,当前在 \(i\) 号点。但是由于点数太大,联通块数最多为 \(n\) ,我们无法记录这么多状态。故开始分类讨论,观察是否能去掉一些冗余的联通块:

  1. 若点数为 \(1\) ,我们肯定不需要记录
  2. 点数为 \(2\) ,由于至少要经过 \(2\)\(b\) 号边才能回到该联通块,所以同样不优。
  3. 点数为 \(3\) 同理
  4. 点数为 \(4\) 时最多会经过 \(3\)\(a\) 边,但 \(3a\) 可能大于 \(2b\) ,所以需要记录。

此时,我们最大的联通块数成功被压缩到了 \(\lfloor\frac{n}{4}\rfloor\) ,使用 \(dijkstra\) 转移,时间复杂度为 \(2^\frac{n}{4}\times n\times \log(2^\frac{n}{4}\times n)\)

CF1810G

题意

你需要生成一个长度不超过 \(n\le5000\) 的数组 \(a\),其中每个 \(a_{i}\) 的取值为 \(1\)\(-1\)

你按照如下方式生成该数组:

  • 首先,你选择一个整数 \(k\)\(1\le k \le n\)),决定数组 \(a\) 的长度。
  • 然后,对于每个 \(i\)\(1\le i \le k\)),你以概率 \(p_{i}\)\(a_{i} = 1\),否则以概率 \(1 - p_{i}\)\(a_{i} = -1\)

数组生成后,你计算 \(s_{i} = a_{1} + a_{2} + a_{3}+ \ldots + a_{i}\),特别地,\(s_{0} = 0\)。然后令 \(S = \displaystyle \max_{i=0}^{k}{s_{i}}\),即 \(S\) 是数组 \(a\) 的最大前缀和。

你会得到 \(n+1\) 个整数 \(h_{0}, h_{1}, \ldots, h_{n}\)。对于最大前缀和为 \(S\) 的数组 \(a\),其得分为 \(h_{S}\)。现在,对于每个 \(k\),你需要求出长度为 \(k\) 的数组的期望得分,对 \(10^9+7\) 取模。

思路

我们第一眼可以自然设出这样一个 \(dp\) 状态:设 \(dp_{i,maxx,now}\) 表示当前考虑到了 \(i\) 号点,最大前缀和为 \(maxx\),当前前缀和为 \(now\) 的概率。此时状态数为 \(n^3\),无法接受。

我们需要了解一个事情:最大前缀和有两种求法,从前往后 和 从后往前。

关于从后往前,由于最大前缀和就是包含 \(1\) 号点的最大子段和,我们设 \(now\) 为在 \(i+1\sim n\) 这一区间内包含 \(i\) 的最大子段和,所以当前枚举 \(a_i\) 的取值时 \(now\) 的值变化为 \(now=max(now+a_i,0)\) ,通过这样的方法我们可以新设置一个 \(dp\)

\(dp_{i,j}\) 表示当前考虑到了第 \(i\) 位,最大子段和为 \(j\) 的概率。此时我们成功压掉了一维状态,但此时我们需要枚举不同的终点,即当考虑数组长度为 \(k\) 时,初始状态应为 \(dp_{k+1,0}=1\) ,答案为 \(\sum_{i=1}^{n}dp_{1,i}\times h_i\)。此时时间复杂度为 \(O(n^3)\) ,依然无法接受。

我们再引入一个新技巧:翻转dp。由于此时终点状态一致,仅有初始状态不同,我们不妨把 \(dp\) 翻转一下,从终点状态出发,走向起点状态。

具体的,我们设 \(g_{i,j}\) 表示考虑到第 \(i\) 位,此时 \(i+1\) 往后的包含 \(i+1\) 的子段提供的最大子段和为 \(j\) ,那么我们此时的初始状态设为 \(g_{0,i}=h_i\),转移有

\[g_{i,j}=g_{i-1,j+1}\times p_{i}+g_{i-1,max(j-1,0)}\times (1-p_i) \]

此时有可能会产生这样的疑惑:如果仅仅钦定了 \([a_i,a_{i+1},…,a_k]\)\(S\) 值为 0,那么有一部分填法中 \([a_i+1,a_{i+2},…,a_k]\)\(S\) 值为 0,另一部分 \(S\) 值为 1,那么为什么 \(g_{i-1,0}\) 能够同时对 \(g_{i,0}\)\(g_{i,1}\) 做等量的贡献?

这时,考虑 dp 定义。我们此时并不知道 \([a_i,a_{i+1},…,a_k]\) 的填法,我们只是要求这一数组的 \(S\) 值为 \(j\)。那么,如果我们在 \(a_i\) 处填了 −1,无论我们要求 \([a_i+1,a_{i+2},…,a_k]\)\(S\) 值填为 0 还是 1,都符合我们对 \([a_i,a_{i+1},…,a_k]\) 的要求,自然都应该进行转移。

启发

  • 前缀和问题,正难则反。
  • 多起点单终点问题,考虑反转 dp。
  • 反转 dp 需要明确状态定义。
posted @ 2025-08-26 18:05  xtp  阅读(12)  评论(0)    收藏  举报