动态规划技巧

一些对于动态规划的技巧,与优化进行区分。

技巧学过之后是简单的,但是不学基本上写不出来,这些技巧一般只是解题的一小步,或者状态的设计与优化,但其实是至关重要的。

1. 费用提前计算

当 DP 中当前决策会影响未来的费用/贡献,且该费用/贡献当前决策相关,这样我们可以提前计算所影响的费用。

栗子:我们有一些机器,存在一个值表示该机器的价值,选出某个机器可以增加其他所有机器的价值 up,则对于选该机器的决策,我们就有固定增加 up×(n1) 的贡献,可以提前计算。

什么叫呢?再举一个例子,若选择 i,j 两个机器会造成额外 (wi+wj)2=wi2+wj2+2wiwj 的贡献,则对于该式子就不仅与 i 相关,因为 2wiwj 与两项皆有关。

例题:P2466 [SDOI2008] Sue 的小球

首先按照 x 排序,这样走一定更优,我们设 f0/1,i,j 表示当前已经拿到 [i,j] 区间内的彩蛋,且当前在 i/j 的最高分数,若我们要花费 t 时间进行移动,则未选择的彩蛋的贡献会减少,即当前选择去某个彩蛋,则对于未来将要造成的贡献是有影响的,且与该决策,或者说花费的时间相关,若是暴力弄的话需要加一维状态,是非常不优的。

考虑费用提前计算,假设我们选了 [i,j] 的点,将要选择 i1 点,则有 yi1t×(k=1i1vk+k=j+1nvk) 的贡献,右边是对未来贡献造成的影响,即所有未选择的彩蛋都下降 t 时间,其中 t 为移动所花费的时间,即 xixi1

所以我们可以得到转移方程:

  • f0,i,j=max(f0,i+1,j(xi+1xi)×si+1,j,f1,i+1,j(xjxi)×si+1,j)+yi
  • f1,i,j=max(f0,i,j1(xjxi)×si,j1,f1,i,j1(xjxj1)×si,j1)+yj

其中 si,j 表示除去 [i,j] 区间内 v 的和,即上文的式子。

复杂度 O(n2)

例题

I P2466 [SDOI2008] Sue 的小球

代码

II P4870 [BalticOI 2009 Day1] 甲虫

双倍经验,该题不用喝完所有露水,我们需要枚举所选个数,复杂度 O(n3)

代码

III UVA1336 Fixing the Great Wall

三倍经验。

代码

IV CF441E Valera and Number

好题。

首先对于 ×2 相当于在其二进制下加了个 0,而 +1 操作相当于进位,这个进位不是很好处理,因为其会大幅影响结尾 0 的个数,我们思考一下,最后的结果都是形如 1000,后面全是 0,而前面我们不关注,也就是说我们只关心其最低位 1 在哪里,所以我们考虑从后往前 DP。

fi,j,k 表示后 i 个操作后,相当于加 j 后,有 k×2概率,则有:

  • 若上个操作为 +1,则因为后面跟着 k×2,相当于 +1,即 fi+1,j+1,kfi,j,k×100p100
  • 若上个操作位 ×2
    • 2j,我们可以相当于加 j2 后,有 k+1×2,即 fi+1,j2,k+1fi,j,k×p100
    • 2j,则其末尾 1 就固定了,而前面我们不关注,所以我们可以直接统计答案,即 ansfi,j,k×p100×k

最后当结束 k 次操作后,其答案与 x 也相关,即 ansfk,i,j×(sx+i+j),其中 sx 表示 x 最低位 0 的个数。

我们最多有 k+1 操作,总复杂度 O(k3),已经可以过了。

可是这与费用提前计算有什么关系呢?我们发现 k 这维是用来计算答案的,而若操作为 ×2 时,其对于后面一定有 1 的贡献,即其二进制末尾一定会多个 0,所以我们可以直接对答案造成 fi,j×p100 的贡献,这样我们减少一维,复杂度 O(k2)

代码

V CF626F Group Projects

一个技巧

首先可以排序,这样对于我们选择的一组学生,其最大值即为最右边的值,则对于每个人有三种情况,最小值/最大值/中间值,所以我们设计 fi,j,k 表示前 i 个学生,还有 j 组没有最大值,所造成贡献为 k 的方案数,有:

  • 若第 i 个人为新的一组最小值,则 fi,j,kfi1,j1,k+ai
  • 若第 i 个人为中间值,则 fi,j,kfi1,j,k×j
  • 若第 i 个人为某一组最大值,则 fi,j,kfi1,j+1,kai×(j+1)
  • 若第 i 个人单独一组,则 fi,j,kfi1,j,k

这里我们将 |maxmin| 的贡献拆开来计算,k 可以为负数,最大可能开到 ai,复杂度 O(n3V),无法通过。

可以发现 k 是较小的,我们考虑将 aiaj 拆分成一些数的和,这样不会造成负数的情况,可以考虑差分,对于状态 fi,j,k,有对于所有没有最大值j 组,一定有 j×(ai+1ai) 的贡献,因为其最大值在 i 之后,一定大于 ai,这里相当于费用提前计算了。

ci=aiai1,有转移:

  • 若第 i 个人为新的一组最小值,则 fi,j,kfi1,j1,kci×(j1)
  • 若第 i 个人为中间值,则 fi,j,kj×fi1,j,kci×j
  • 若第 i 个人为某一组最大值,则 fi,j,k(j+1)×fi1,j+1,kci×(j+1)
  • 若第 i 个人单独一组,则 fi,j,kfi1,j,kci×j

这样复杂度是 O(n2k)

代码

VI AT_abc219_h Candles

II 的加强,唯一的区别是本题的蜡烛长度会不同,这样会导致我们选择的熄灭的蜡烛会不是一段连续的区间,我们就不可以枚举区间,考虑到我们费用提前计算的是未熄灭的蜡烛,即这些蜡烛都会变短,这样我们在其基础上加一维 k 表示 [l,r] 区间外还剩 k 只蜡烛未被熄灭。

转移类似,多了两种不熄灭该蜡烛的情况需要讨论,复杂度 O(n3)

代码

VII P7650 [BalticOI 2007 Day 1] Ranklist Sorting

神题。

分数各不相同,先离散化好处理,这里将其从大到小排序(意思是最大值为 1,次大值为 2 ...),令 idi 表示数 i 在原数列所在下标,ai 表示下标为 i 中的数。

所以最后我们会得到 (1,2,....n1,n) 的数列。

首先观察题目,可以感性发现一些性质。

  • 若要使某个人 x 移动,最多让其移动 1 次,即移动到 x+1 前面,移动多次一定不如只移动一次优。
  • 对于某个人 x,若其向后移动,会使一些人的下标减少,可以减少费用。

可以得出一个 结论:我们的操作应该是从编号大的到编号小的进行操作,且每个人最多操作一次

对于某个人 i,我们假设所有 >i 的数已经聚到一起,则我们可以设计 fi,j 表示到第 i 个人,且所有 i 的人聚到 j 的位置的最小费用。

分为两种情况。

  • 若其要向右移动,设其要到达的位置为 j>idi

    因为每次所造成的贡献是当前下标,所以我们需要考虑求出,因为我们从大到小操作,根据结论可知,所有小于 i 的数都没有动,即 i 所在位置前面小于 i 的数是可知的,而所有大于 i 的数一定在右边,因为 j>idi,所以 i 所在的真实位置是下标小于 idi 且大小小于 i 的数目 +1,设 ci,j 表示前 i1 个数中 <j 的数目,则当前 i 所在真实位置即为 cidi,i+1,同理我们可以得出 i+1 的真实位置为 cj,i+1+1

    • 则若移动至 j 前面的贡献cidi,i+cj,i+1+1

      所以有 fi,j=fi+1,j+cidi,i+cj,i+1+1=fi+1,j+cidi,i+1+cj,i+1,j>i

    • 因为 j>idi,所以其实我们可以不移动,让在 (idi,j) 中间的数向前移动,但是该决策会影响向左移动的人的费用,我们考虑费用提前计算,对于 k(idi,j),我们计算 k 只计算了下标小于 k大小小于 ak,的值,所以若 ak<ik 还需要跳过 iak 个数,因为我们已经假设所有 >i 的数已经聚到一起,即所有 >ak 的数都在 k 之前。

      所以有 fi,idi=minj=i+1n(fi+1,j+k=i+1,i>akj1iak)

  • 若其要向左移动,设其要到达的位置为 j<idi

    去除掉不移动的人的影响,这种情况必须移动,所以贡献为 cidi,i+1+cj,i+1

总转移有:

  • fi,j=fi+1,j+cidi,i+1+cj,i+1
  • fi,idi=minj=i+1n(fi+1,j+k=i+1,i>akj1iak)

答案即为 mini=1nf1,i

这里为了方便,添加了一个 n+1 的数放到数列末尾,对于输出方案我们记录来源,找出下标在其之前的数的个数,求出真实下标即可。

复杂度 O(n2)

代码

2. 假设未来决策

3. 状态设计

状态设计是 DP 的核心。

例题

I 4074. 二分的代价

首先有显然的 O(n3) 的区间 DP,但是只有 10pts : (。

对于固定 l 的区间 [l,r],显然有,越长的区间所需费用更高,而我们又发现答案很,最大只能达到 9logn,所以可以设 fi,j 表示以 i 为左端点,使得 [i,r] 所需费用 j最大 r,这样对于 i,只有当 fi,jakk1,此时可以 fi,jfk+1,jai,这样复杂度是 O(9n2logn),考虑继续优化。

我们将枚举 k 变为枚举 w=ak,则其实我们本质上是找 kfi,jw+1,最大的 k 使得 ak=w,这个东西可以直接预处理。

即得 fi,jftmp+1,jw,其中 tmp=prefi,jw+1,w

复杂度 O(81nlogn),貌似还可以优化一个 9,but I don't know.

代码

参考文章

对一类动态规划问题的研究 - 徐源盛 2009

浅谈一类优化思想:费用提前计算 - LgxTpre

posted @   oXUo  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
网站统计
点击右上角即可分享
微信分享提示