解题报告——论对“数位 dp”的新理解

解题报告——论对“数位 dp”的新理解

以前我总是对这个东西心存畏惧,但是今天打了一道题之后发现它好像不是那么难以理解。我们一步一步剖析它。

首先,数位 dp 是什么?给定一个区间 [L,R],求区间内满足某些条件的数的数量。这些条件通常跟数位有关,我们务必要把这个数拆开。

题目提问说让我们求 [L,R] 之间满足条件的数的数量,我们何不把它看成 [0,R] 之间满足条件的数的数量减去 [0,L1] 之间满足条件的数的数量?这就是数位 dp 的第一步。

第二步,我们把数一个一个往上数,比如从 20002999,30003999,40004999,这样 000999 就数了三遍,这很明显是可以优化的。我们已经深入到了数位 dp 的核心。

第三步,数位 dp 的状态设置。最朴素的数位 dp 一般只会设置一个量,就是 fdep,其中 dep 表示第几位。但是根据题目情况的不同,为了满足条件,我们一般设置多个量,其中一个是 dep,另外一大堆就视题目而定了。

第四步,数位 dp 的实现方式。由于是数位 dp,要一位一位地 dp,我们一般采用记忆化搜索的方式一位一位往下搜。那么搜索的时候又应该设置什么量呢?除了前面状态表示中的一堆量,我们还要额外设置两个特殊的 bool 量:一个 lim 用来记录是否顶到上界,一个 lead 用来记录当前位是否是前导零。这两个量的实际用处视题目不同而不同,但是一般都要设置。此处额外提醒,如果 lim 或者 leadtrue,那么当前状态得到的 dp 结果不能记录入 f 数组中。

第五步,规范一下代码吧。数位 dp 一般分几块来写,条理清晰:

  • 边界条件。比如,dep=0,搜到最后一位外面去了。
  • 记忆化。如果当前状态的 limlead 都是 false 并且当前位置对应的 f 已经有值了,就直接返回。
  • 向下搜索。这是最重要的一步,由当前状态,往下递推,所有状态都会因为这个增加的数而发生变化。
  • 存储答案。特别的,如果 lim 或者 leadtrue,不能存储答案。

最后说一个遇到的易错点。如果题目问话只给上界,一定要看下界是 1 还是 0。大多数数位 dp 过程中都会把 i=0 的情况算入答案,这个时候要考虑一下是否要去掉。

这种数位 dp 入门题,曾经能够卡我 3 个小时,今天往后不会了。但是有一说一,也正是这道题让我悟出了数位 dp 的核心。

posted @   KarmaticEnding  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示