首页 > 其他分享 >动态规划技巧

动态规划技巧

时间:2024-11-12 10:32:07浏览次数:1  
标签:贡献 动态 技巧 移动 times 我们 gets 规划 id

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

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

1. 费用提前计算

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

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

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

例题:P2466 [SDOI2008] Sue 的小球

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

考虑费用提前计算,假设我们选了 \([i,j]\) 的点,将要选择 \(i-1\) 点,则有 \(y_{i-1} - t \times \left(\sum_{k=1}^{i-1}v_k + \sum_{k=j+1}^n v_k\right)\) 的贡献,右边是对未来贡献造成的影响,即所有未选择的彩蛋都下降 \(t\) 时间,其中 \(t\) 为移动所花费的时间,即 \(x_i - x_{i-1}\)。

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

  • \(f_{0,i,j} = \max(f_{0,i+1,j} - (x_{i+1} - x_i) \times s_{i+1,j},f_{1,i+1,j} - (x_j - x_i) \times s_{i+1,j}) + y_i\)
  • \(f_{1,i,j} = \max(f_{0,i,j-1} - (x_j - x_i) \times s_{i,j-1},f_{1,i,j-1} - (x_j - x_{j-1}) \times s_{i,j-1}) + y_j\)

其中 \(s_{i,j}\) 表示除去 \([i,j]\) 区间内 \(v\) 的和,即上文的式子。

复杂度 \(\mathcal{O}(n^2)\)。

例题

I P2466 [SDOI2008] Sue 的小球

代码

II P4870 [BalticOI 2009 Day1] 甲虫

双倍经验,该题不用喝完所有露水,我们需要枚举所选个数,复杂度 \(\mathcal{O}(n^3)\)。

代码

III UVA1336 Fixing the Great Wall

三倍经验。

代码

IV CF441E Valera and Number

好题。

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

设 \(f_{i,j,k}\) 表示后 \(i\) 个操作后,相当于加 \(j\) 后,有 \(k\) 个 \(\times 2\) 的概率,则有:

  • 若上个操作为 \(+ 1\),则因为后面跟着 \(k\) 个 \(\times 2\),相当于 \(+ 1\),即 \(f_{i+1,j+1,k} \gets f_{i,j,k} \times \frac {100 - p} {100}\)
  • 若上个操作位 \(\times 2\)。
    • 若 \(2\mid j\),我们可以相当于加 \(\frac j 2\) 后,有 \(k+1\) 个 \(\times 2\),即 \(f_{i+1,\frac j 2,k+1} \gets f_{i,j,k} \times \frac p {100}\)。
    • 若 \(2\nmid j\),则其末尾 \(1\) 就固定了,而前面我们不关注,所以我们可以直接统计答案,即 \(ans \gets f_{i,j,k} \times \frac p {100} \times k\)。

最后当结束 \(k\) 次操作后,其答案与 \(x\) 也相关,即 \(ans \gets \sum\sum f_{k,i,j} \times (s_{x + i} + j)\),其中 \(s_x\) 表示 \(x\) 最低位 \(0\) 的个数。

我们最多有 \(k\) 次 \(+ 1\) 操作,总复杂度 \(\mathcal{O}(k^3)\),已经可以过了。

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

代码

V CF626F Group Projects

一个技巧

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

  • 若第 \(i\) 个人为新的一组最小值,则 \(f_{i,j,k} \gets f_{i-1,j-1,k+a_i}\)。
  • 若第 \(i\) 个人为中间值,则 \(f_{i,j,k} \gets f_{i-1,j,k} \times j\)。
  • 若第 \(i\) 个人为某一组最大值,则 \(f_{i,j,k} \gets f_{i-1,j+1,k-a_i} \times (j + 1)\)。
  • 若第 \(i\) 个人单独一组,则 \(f_{i,j,k} \gets f_{i-1,j,k}\)。

这里我们将 \(|max - min|\) 的贡献拆开来计算,\(k\) 可以为负数,最大可能开到 \(\sum a_i\),复杂度 \(\mathcal{O}(n^3V)\),无法通过。

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

设 \(c_i = a_i - a_{i-1}\),有转移:

  • 若第 \(i\) 个人为新的一组最小值,则 \(f_{i,j,k} \gets f_{i-1,j-1,k-c_i\times (j-1)}\)。
  • 若第 \(i\) 个人为中间值,则 \(f_{i,j,k} \gets j \times f_{i-1,j,k-c_i\times j}\)。
  • 若第 \(i\) 个人为某一组最大值,则 \(f_{i,j,k} \gets (j+1) \times f_{i-1,j+1,k-c_i\times (j+1)}\)。
  • 若第 \(i\) 个人单独一组,则 \(f_{i,j,k} \gets f_{i-1,j,k-c_i\times j}\)。

这样复杂度是 \(\mathcal{O}(n^2k)\)。

代码

VI AT_abc219_h Candles

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

转移类似,多了两种不熄灭该蜡烛的情况需要讨论,复杂度 \(\mathcal{O}(n^3)\)。

代码

VII P7650 [BalticOI 2007 Day 1] Ranklist Sorting

神题。

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

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

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

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

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

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

分为两种情况。

  • 若其要向右移动,设其要到达的位置为 \(j > id_i\):

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

    • 则若移动至 \(j\) 前面的贡献为 \(c_{id_i,i} + c_{j,i+1} + 1\)。

      所以有 \(f_{i,j} = f_{i+1,j} + c_{id_i,i} + c_{j,i+1} + 1 = f_{i+1,j} + c_{id_i,i} + 1 + c_{j,i} + 1,j > i\)。

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

      所以有 \(f_{i,id_i} = \displaystyle\min_{j=i+1}^n(f_{i+1,j} + \displaystyle\sum_{k=i+1,i>a_k}^{j-1}i - a_k)\)。

  • 若其要向左移动,设其要到达的位置为 \(j < id_i\)。

    去除掉不移动的人的影响,这种情况必须移动,所以贡献为 \(c_{id_i,i} + 1 + c_{j,i} + 1\)。

总转移有:

  • \(f_{i,j} = f_{i+1,j} + c_{id_i,i} + 1 + c_{j,i} + 1\)。
  • \(f_{i,id_i} = \displaystyle\min_{j=i+1}^n(f_{i+1,j} + \sum\limits_{k=i+1,i>a_k}^{j-1}i - a_k)\)。

答案即为 \(\displaystyle\min_{i=1}^n f_{1,i}\)。

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

复杂度 \(\mathcal{O}(n^2)\)

代码

2. 假设未来决策

参考文章

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

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

标签:贡献,动态,技巧,移动,times,我们,gets,规划,id
From: https://www.cnblogs.com/HaoXu-qwq/p/18541302/DP-trick

相关文章

  • PMP–一、二、三模、冲刺–分类–6.进度管理--技巧--赶工&快速跟进
    文章目录技巧一模6.进度管理--6.控制进度--赶工和快速跟进的区分--赶工:增加资源,以最小的成本代价来压缩进度工期;快速跟进:将正常情况下按顺序进行的活动或阶段改为至少是部分并行开展。【赶工加人,快速跟进多开工】6.进度管理--6.控制进度--进度压缩--采用进度压缩技术使进......
  • 安卓解压缩软件推荐:最佳选择和实用技巧
    在安卓系统中,解压缩软件具有至关重要的意义。随着移动互联网的飞速发展,我们在日常生活和工作中经常会接触到各种压缩文件。无论是接收工作文档、下载学习资料,还是获取娱乐资源,都可能以压缩包的形式出现。解压缩软件在文件管理方面发挥着关键作用。它可以将大量文件压缩成较小......
  • 国标GB28181软件LiteGBS国标GB28181公网直播动态IP的网络摄像头如何接入
    在功能方面,与一些传统的视频监控软件相比,LiteGBS国标GB28181软件支持的功能更加丰富多样。它不仅具备基本的视频监控直播、录像检索与回看功能,还拥有云台控制、语音对讲、告警上报、平台级联等高级功能。在社会资源接入项目中,有较多的单位不具备固定外网IP入网的条件。尤其像民办......
  • LangGraph的两种基础流式响应技巧
    在构建复杂的AI应用时,LangGraph作为一个强大的工具,为我们提供了灵活的图结构程序设计能力。今天,我们将深入探讨LangGraph中的一个关键特性:流式响应模式。这个特性不仅能提高应用的响应速度,还能为用户提供更加流畅的交互体验。LangGraph中的流式响应:与传统LLM有何不同?在LangGraph......
  • 电脑提示kernel32.dll动态链接库报错怎么解决?kernel32.dll修复方法
    在使用电脑的过程中,你是否遇到过kernel32.dll动态链接库报错的情况呢?这个问题可能会让你感到困扰,但别担心,今天我们就来一起探讨一下kernel32.dll动态链接库报错的解决方法。一、了解kernel32.dllkernel32.dll是Windows操作系统的核心动态链接库之一,它包含了许多重要......
  • 动态规划-背包问题——416.分割等和子集
    1.题目解析题目来源416.分割等和子集——力扣测试用例 2.算法原理1.状态表示这里背包问题基本上和母题的思路大相径庭,母题请见[模板]01.背包 ,这里的状态表示与装满背包的情况类似,第二个下标就是当选择的物品体积直接等于j时是否可以装入"背包",本题是求是否......
  • 运维人员必备的 Mac Zsh 配置技巧
    作者:SRE运维博客博客地址:https://www.cnsre.cn/文章地址:https://www.cnsre.cn/posts/241022203423/相关话题:https://www.cnsre.cn/tags/aws/运维人员必备的MacZsh配置技巧作为一名运维工程师,我们在日常工作中经常需要与AWS打交道。为了提高工作效率,我在Mac的......
  • 深入理解Java动态代理:原理、实现与应用
    深入理解Java动态代理:原理、实现与应用在现代软件开发中,面向对象编程(OOP)和面向切面编程(AOP)是两种重要的编程范式。Java语言中的动态代理(DynamicProxy)是实现AOP的关键技术之一,它允许我们在运行时创建一个代理对象,该代理对象可以拦截对真实对象方法的调用,并在方法调用前......
  • DP 技巧总结
    DP技巧总结进行了为期接近一周的DP特训,做了很多同学找的高质量题目,也学到了很多技巧。现在来把一些感觉比较有价值的技巧进行一些统一的总结。插入型DP这个东西之前应该在选拔期间的一场周测中见过,但是隔了很久,已经有所遗忘。这次题单里出现了两道类似的题目,我都不会做。其......
  • 2024年华为OD机试真题-光伏场地建设规划 -C++-OD统一考试(E卷)
    最新华为OD机试考点合集:华为OD机试2024年真题题库(E卷+D卷+C卷)_华为od机试题库-CSDN博客     每一题都含有详细的解题思路和代码注释,精编c++、JAVA、Python三种语言解法。帮助每一位考生轻松、高效刷题。订阅后永久可看,发现新题及时跟新。题目描述祖国西北部有一片大片......