数位DP

一、数位是指把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。

如果拆的是十进制数,那么每一位数字都是 0~9,其他进制可类比十进制

数位 DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:

1.要求统计满足一定条件的数的数量(即,最终目的为计数);

2.这些条件经过转化后可以使用「数位」的思想去理解和判断;

3.输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;

4.上界很大(比如 10^{18}),暴力枚举验证会超时。


二、数位 DP 的基本原理:

考虑人类计数的方式,最朴素的计数就是从小到大开始依次加一。但我们发现对于位数比较多的数,这样的过程中有许多重复的部分。
例如,从 7000 数到 7999、从 8000 数到 8999、和从 9000 数到 9999 的过程非常相似,它们都是后三位从 000 变到 999,不一样的地方只有千位这一位,所以我们可以把这些过程归并起来,将这些过程中产生的计数答案也都存在一个通用的数组里。此数组根据题目具体要求设置状态,用递推或 DP 的方式进行状态转移。

数位 DP 中通常会利用常规计数问题技巧,比如把一个区间内的答案拆成两部分相减:

即a(l,r)=a(r,0)-a(l-1,0);


三、那么有了通用答案数组,接下来就是统计答案。

统计答案可以选择记忆化搜索,也可以选择循环迭代递推。
为了不重不漏地统计所有不超过上限的答案,要从高到低枚举每一位,再考虑每一位都可以填哪些数字,最后利用通用答案数组统计答案。


例题1

https://www.luogu.com.cn/problem/P2602

解法

先dp出满i位的数的数位数量(有前导0)
再根据上限分位从高到低算出数量,去除前导0的0;

上图:关于具体求数位的过程
image

Code

点击查看代码
int a, b;                                     // 待求区间a,b
int cnt_a[10], cnt_b[10], dp[15], mi_10[15];  // mi_10[i]为10的i次幂
// cnt[i]为数i的数量 //dp[i]为满前i位数的每个数的数量
int x_wei[15];                // 记录数x每一十进制位
void swdp(int x, int* cnt) {  // 数位dp
    int tmp = x;
    int len = 0;
    while (x) {
        x_wei[++len] = x % 10;
        x /= 10;
    }
    // 实质上每一次i计算的是对应位数上限的数量
    for (int i = len; i >= 1; i--) {
        for (int j = 0; j <= 9; j++) {  // 加上 0到a[i]*10^(i-1)-1的 前i-1位的个数
            cnt[j] += x_wei[i] * dp[i - 1];
        }
        for (int j = 0; j < x_wei[i]; j++) {  // 加上 第i位的个数
            cnt[j] += mi_10[i - 1];
        }
        tmp -= x_wei[i] * mi_10[i - 1];  // tmp为前i位的上界数量
        cnt[x_wei[i]] += tmp + 1;        // 对第i位加 1是mi_10[i-1];
        cnt[0] -= mi_10[i - 1];          // 减去前导0
    }
}
void solve() {
    cin >> a >> b;
    mi_10[0] = 1;
    // dp[i]的每一个数 数量相同
    for (int i = 1; i <= 13; i++) {                // 对满i位的数量dp统计
        dp[i]    = 10 * dp[i - 1] + mi_10[i - 1];  // mi_10加上第i位的情况
        mi_10[i] = 10 * mi_10[i - 1];
    }
    swdp(b, cnt_b);
    swdp(a - 1, cnt_a);
    for (int i = 0; i <= 9; i++) {
        cout << cnt_b[i] - cnt_a[i] << ' ';
    }
}
posted @ 2024-07-17 19:29  uanQ  阅读(1)  评论(0编辑  收藏  举报