zoukankan      html  css  js  c++  java
  • 一类动态规划问题状态的简化

    DP学习心得—状态的设计与简化

    其实是带着一些奇怪的想法找了点题目。

    动态规划由两个重要的部分组成:转移方程状态
    好的状态是动态规划能够发挥威力的重要前提,而好的转移方程则是动态规划发挥作用的途径与方式
    因此,通过一段时间的学习,本人试着总结出一些动态规划问题的状态上的设计思路与巧妙化简,并写下这篇总结供日后学习所需。


    Part 1 状态的设计

    好的状态是动态规划的开始,一个动态规划问题中的状态应该首先满足:

    1. 能够准确地描述对转移有影响的状态维度,不存在不同转移情况的混淆(即转移过程不遗漏,不混淆)。
    2. 不具备后效性,即不会存在互相转移的情形。
    3. 能够满足转移时最优性的传递(即最优子结构性)。

    在满足了以上几点后,我们基本可以得到一个保证正确性的状态。
    但是有时候,仅仅这样是不够的,我们的状态可能会显得过于冗杂,此时则需要我们用一定的技巧来转变思考角度,减少冗杂的成分,来让转移更加的轻简。

    Part 2 状态的简化

    状态的优化大致上分为两种类型:修正型和简化型。
    接下来将从两个方面,通过一些例题来总结一些思路与方式。


    首先是两道较为简单的基础优化。(注意是优化较为基础)

    Sharing Chocolates

    题意简述:给定两个正整数 (n,m) ,问是否能够将一个长,宽分别为 (n,m) 的矩形通过每次沿长或宽的方向分成两个子矩形,使得最后恰好得到 (k) 个子矩形,且面积分别为 (a_1,a_2,a_3,...,a_k)
    (tips:n,mle100,kle15)

    首先,我们考虑一个较显然的 DP 思路。既然 (k) 只有小小的 (15) ,我们考虑把当前矩形最终可以得到的 (a_1,..,a_k) 中的哪些面积当成一维写入我们的状态,这样不难得出一个状态:

    [dp[i][j][sta] ]

    表示矩形的长为 (i) ,宽为 (j) 时,是否可以通过划分得到 (sta) 中的面积。
    这样一来,我们就可以得到一个总数为 (O(mn2^k)) 的状态。
    但是这个状态在空间上就已经难以接受,因此,我们需要考虑优化。

    注意到一点:如果一个状态 (sta) 中所有元素之和不为 (icdot j),可以直接为 (false)
    这是不难理解的,因为我们的要求是恰好得到,如果面积都不等就不可能恰好得到了。
    这样一来,我们便有了一个优化思路:我们可以通过 (sta)(i) 的值来直接算出 (j) !也就是说状态中的 (j)可以被其他维度表示的多余维度。发现这个后我们就不难修正出新的状态了:

    [dp[i][sta] ]

    表示当前矩形较长的一条边长度为 (i) (这样规定方便处理),是否可以通过划分得到 (sta) 中的面积。
    这样,我们的状态总数就降到了 (O(max(n,m)cdot2^k)) ,可以满足空间的要求了。

    Blinker 的仰慕者

    题意简述:给定两个正整数 (L,R) ,求区间 ([L,R]) 中满足各位数字之积等于 (k) 的数的和。
    (tips:L,R le 10^{18},k le 9^{18})

    这道题的状态是不难考虑的,也是很套路的数位 DP
    (dp[i][j]) 表示考虑了前 (i) 位,当前数字各位乘积为 (j) 的数字之和;
    (f[i][j]) 表示考虑了前 (i) 位,当前数字各位乘积为 (j) 的数字个数。
    则我们可以很方便地写出转移方程...慢着!
    (k) 不是最大可以有 (9^{18}) 吗,这怎么直接弄进状态了?!

    是的,这样大的数据范围不允许我们把乘积写入状态,但是又不可以直接把这一维给省略掉,这该如何是好呢?
    幸运的是,我们发现乘积只会由 ([1,9]) 中的数字相乘而来,这就意味着每一个乘积都可以被分解为 (2^acdot3^bcdot5^ccdot7^d) 的形式!
    而通过计算得知,([2,10^{18}]) 内满足此条件的数不超过 (10^5)
    于是,我们可以预处理所有的 (2^acdot3^bcdot5^ccdot7^d) ,然后就可以把大量的无用状态通过离散的方式舍去,这样就可以通过这道题了。


    完成了这两道试手题,我们来看些有难度的。

    义已失吾亦死

    题意简述:给定 (n,a,b,c,p) ,求用 (a)(1)(b)(4)(c)(5) 能够组成的最大的能被 (p) 整除的 (n) 位自然数。
    (tips:a,b,c,nle233,ple64)

    这道题乍一看不蛮好想,但我们大概确定了方向:数位 DP
    接下来考虑状态的设置。
    首先我们可以排除掉最优性 DP,因为数位在这个问题中并不具备最优子结构(后面尽可能大不一定最后也是最大)。
    那么我们考虑设状态 (dp_{i,a,b,c,q}) 表示考虑了前 (i) 个数位,还剩余 (a,b,c)(1,4,5) ,当前组成的数模 (p) 的值是否可以(q)
    那么我们就可以考虑 DP 计算了,最后统计答案可以贪心地在每一位尽可能往高了选。
    但是有一个问题:状态总数是 (O(n^4p)) 的,过不了怎么办?

    方法一:简化状态

    发现一个细节:我们可以直接通过 (a,b,c) 来算出 (i) 的值来。
    这就帮助我们消去了许多无用状态,总数为 (O(n^3p))

    方法二:压缩状态

    我们每一个状态表示的都是是否可以的形式,意味着它的取值只有 (0/1)
    那么不难发现大量空间被浪费了,又注意到 (ple64) ,所以我们考虑把状态变为 (dp_{a,b,c}) 表示已经用了 (a,b,c)(1,4,5) 后组成的数模 (p) 的余数的集合,即一个状态压缩后的整数((64) 位,每位为 (0/1) 表示是否存在模 (p) 后为这个数的数)。
    这样的话状态总数成为 (O(n^3)) ,总的复杂度就会降为 (O(n^3)) ,可以通过本题。

    Easy Climb

    题意简述:给定一个长度为 (n) 的序列 (a) ,每次可以选择一个数 (a_i(i in [2,n-1])) 花费 (1) 的代价把它变成 (a_i+1)(a_i-1) ,求使得对于任意 (iin[1,n)) 满足 (|a_i-a_{i+1}|) 不超过 (d) 的最小代价。
    (tips:2le nle100,0le d,a_ile10^9)

    这个题首先有一个很显然的暴力 dp 的思路。
    (dp[i][j]) 表示考虑了前 (i) 个数,使得当前数的大小为 (j) 的最小代价。
    那么我们就可以快乐的 (O(nd^2)) 计算啦!(离谱)
    那我们就应该考虑怎么优化。
    首先我们并不可以离散化,因为它的修改是任意的,无法直接离散化。
    考虑把状态的冗杂部分去除?但是这两个状态都是不可抹去的,不能互相推导。
    或许考虑换一下状态的定义?在本题中这种方法并不适用,无法起到优化复杂度的作用。
    这个时候,我们就应该进行对问题的分析,运用贪心思想来优化状态。

    假设现在有三个数字 (a,b,c) ,考虑 (b) 的变化范围应该是: ([max(a,c)-d,min(a,c)+d])
    那么 (b) 的取值就只有三种情况:

    1. 不变。
    2. (max(a,c)-d)
    3. (min(a,c)+d)

    由此我们可以发现一个结论: (b) 的最后结果一定可以表示为 (p+kcdot d) ,其中 (p)(a,b,c) 中的一个数。
    那么这个想法是否适用于 (a_1,a_2,...,a_n) 呢?答案是肯定的。
    即:所有 (a_i) 最后都可以表示为 (a_p+kcdot d) ,其中 (pin[1,n],kin[-2n,2n])
    这个想法的正确性证明参考这篇博客(不想画图)。
    那么就可以通过贪心思想来帮助我们实现离散化的操作。
    (dp[i][j]) 表示考虑了前 (i) 个数,使得当前数的大小离散化后为 (j) 的最小代价。
    那么状态总数就是 (O(n^3)) 的,已经优化了不少,下一步是很经典的单调队列优化,不再赘述。


    Summary

    综合来看,dp 的状态简化一般有如下方法:

    1. 消除冗杂量。
    2. 状态离散化。
    3. 位运算压缩状态。
    4. 贪心等其他算法辅助。

    一般的针对状态的优化大概就是这四种类型,其中除最后一点需要深入思考题目性质外,其他三点都是比较容易发现的,当转移方面已经无路可走时,不妨想一想能否从状态本身下手来进行优化。

  • 相关阅读:
    Navicat Premium 12.1.12.0破解版激活
    vConsole调试器
    使用DbFunctions来解决asp.net mvc ef按照日期分组数据
    谷歌浏览器如何安装CRX插件?crx离线插件安装方法
    ASP.NET MVC——CodeFirst开发模式
    Sql server 事务的两种用法
    SQL Server 存储过程
    JqueryMobile新手问题大全
    .net core 轻量级容器 ServiceProvider 源码分析
    Centos7 使用Docker 部署mssql 2017
  • 原文地址:https://www.cnblogs.com/tuifei-oiers-home/p/14226145.html
Copyright © 2011-2022 走看看