数位动态规划是一类特殊的动态规划,它的形式一般为:给定下界$l$和上界$r$,求$[l,r]$之间满足某一要求的元素个数。一般来说,数位动态规划有一个特定的状态表达:$f[pos][stats][bound]$。简单对$pos$,$stats$,$bound$做一个说明:
Pos: 第一维表示现在处理到了数的第pos位。在数位dp中,一般从数的高位到低位,一位一位的处理下去。例如当 $l = 13$,上界 $r = 678$的时候,我们会将上下界的高位补零使得它们有相同的位数,既$(l,r) = (013,678)$。随后从高位开始进行dp。
Stats: 在$pos$位之前的哪些数的状态被压缩成了$stats$。举个简单的例子,假设我们要求出$[l,r]$之间各位数字之和是$5$的倍数的数的个数,那么此$stats$就可以表示为“在pos位之前那些数的数字之和为mod 5 的值”,这样我们只有一位就可以表示$pos$位之前的状态。如果在第${pos}$位我们选择了数d,那么动态规划中的下一个状态就为${f}[pos+1][(stats+mod) mod 5][...]$。
Bound: 在$pos$位之前的那些数字和$l,r$的关系为$bound$ 。继续举例子,假如要求出[013, 678]之间各位数字之和是5的倍数的数的个数:
- 如果最高位选择了2,它和上下界的最高位数没有任何关系,因此对于次高位的数字,我们可以在[0,9]之间任意选择。
- 如果最高位选择了6,此时这个数字是贴着上界的,也就是说对于次高位置的数字,我们只能在$[0,7]$之间选择,其中7就是上界的次高位的数字。
- 如果最高位选择了0,此时这个数字是贴着下届的,也就是说对于次高位置的数字,我们只能在$[1,9]$之间选择,其中1是下界的次高位的数字。
- 此外,还有一种最为特殊的情况。如果上下界为$ (l, r) = (123, 156)$,并且最高位我们选择了 1,那么此时这个数字既「贴着」上界,也「贴着」下界,对于次高位的数字,我们只能在 [2, 5]之间选择,其中 2 是下界的次高位数字,5 是上界的次高位数字。
综上所述,$bound$共有4中不同的取值情况,分别为0,1,2,3,解释如下:
- 如果和上下界没有任何关系,那么取值为 0,并且以后也不可能和上下界有关系;
- 如果「贴着」下界,那么取值为 1,并且只有当第$pos$ 位取了下界对应位置的数字时,才会延续 1 值,否则会变为 0;
- 如果「贴着」上界,那么取值为 2,并且只有当第 $pos$ 位取了上界对应位置的数字时,才会延续 2 值,否则会变为 0;
- 如果同时「贴着」上下界,那么取值为 3,并且:如果 $pos$ 位同时取了上下界对应位置的数字(此时上下界对应位置的数字一定相同),那么延续 3值;如果$ pos $位取了下界对应位置的数字,那么会变为 1;如果$ pos$ 位取了上界对应位置的数字,那么会变为 2;其余情况会变为 0。
而对于$ f[pos][stats][bound]$ 的值本身,它表示「满足上述条件的数的个数」。这样以来,我们可以使用记忆化搜索(DFS + memo)的方法进行动态规划即:$f[pos][stats][bound] = sum_{d} f[pos+1][g_{s}(stats,d)][g_{b}(bound,d)]$。
其中$d$为第$pos$位选择的数字;$g_{b}(bound,d)$是关于bound的转移函数,例如上文中0,1,2,3之间的转化;$g_{s}(stats,d)$是关于${stats}$的转移函数。最终答案存放在$f[0][stats_{init}][3]$中,即满足「枚举到最高位、初始状态、同时贴着上下界(可以想象成更高位还可以补零,那么在最高位之前的数字都是贴着上下界的)」的数的个数,也就是需要求出的答案。
例题的话可以看https://i-beta.cnblogs.com/posts/edit;postId=12689547