数位统计 dp 问题

数位统计dp是一种处理与数字的各个数位相关的动态规划问题。常见应用包括在指定区间内满足特定条件的数字数量。递推和记忆化搜索是解决这类问题的两种主要方法,其中记忆化搜索能有效避免重复计算,简化问题。文章提供了多个例题,如统计问题、手机号码问题等,展示如何运用数位dp解决实际问题。
摘要由CSDN通过智能技术生成

数位统计 dp


简介

数位dp启蒙老师:数字组成的奥妙——数位dp - Mathison 的博客 - 洛谷博客 (luogu.com.cn)

数位统计dp,简称数位dp,指的是一种与每个数字的数位上有关的动态规划问题。其中,数位既可以是基于十进制,也可以基于二进制等等。

解决的问题

数位dp能解决什么问题?

数位dp通常能解决的问题有这样大致的题意:给定一个区间 [ L , R ] [L,R] [L,R],问在这个范围内能够使得给定条件 f ( i ) f(i) f(i)满足的个数。不同的问题可能会有不同,不过如果满足上述问题时则可以尝试使用数位dp解决问题。

另外一方面,因为在数位dp中,是按照数的每一个数位进行dp,因此在数位dp相关问题中给定的数据范围一般会很大,暴力的方法会TLE,而使用动态规划的方式则会使得过程的重复子问题被精简掉。

写法

递推

数位dp的递推写法,也就是考虑对当前枚举的这一位数字,要考虑到它对这三个状态的影响:

  1. 1 1 1位至第 i − 1 i-1 i1位(可能没有)
  2. i i i
  3. i + 1 i+1 i+1位至最后一位(可能没有)

考虑这三个大的子问题后,还要考虑其中的子问题:

  • 可以有多少个数字的“放法”
  • 是否受到前导零的影响

但是由于记忆化搜索在数位dp上的应用反而会使得过程清晰化,所以其实推荐的还是记忆化搜索。

记忆化搜索

众所周知,记忆化搜索通过将某个状态记录下来,从而避免重复对状态的搜索。对于数位统计dp问题中,显然会存在非常多的重复子问题。因此,记忆化搜索很适合于数位dp中。并且,记忆化搜索还可以有“模板”供于使用。

using ll = long long;
ll f[35][35];
int digit[35], len;
ll dfs(ll pos, ll st, ..., bool lead, bool limit) {
   
    /* 搜索结束,该返回点东西 */
    if (pos > len) {
   
        return ?;
    }
    /* 符合的状态搜过,结束搜索 */
    if (f[pos][st]...[] != -1 && !lead && !limit) {
   
        return f[pos][st]...[];
    }
    /* 继续搜索,同时结果先暂存 */
    int LIM = limit ? digit[pos] : 9;
    ll ret = 0;
    for (int i = 0; i <= LIM; ++ i) {
   
        /* 各种条件判断,比如前导0 */
        if () {
   
            
        } else {
   
            ret += dfs(pos + 1, st + (?), ..., lead && i == 0, limit && i == LIM);
        }
    }
    /* dp数组存的是没有前导零和达到边界的状态 */
    if (!lead && !limit) {
   
        f[pos][st]...[] = ret;
    }
    return ret;    
}
ll solve(ll n) {
   
	memset(f, 0, sizeof f);
    m = 0;
    while (n > 0) {
   
        digit[++ m] = n % 10;
        n /= 10;
    }
    reverse(digit + 1, digit + 1 + m);
    return dfs(1, 0, ..., true, true);
}

其中,对于limit,也就是上一位是否达到最大数字的表示,可以用limit && i == LIM进行状态转移;同时,对于lead,也就是上一位是否是0,可以用lead && i == 0进行状态转移。

例题

例题 统计问题 The Counting Problem

给定两个整数 a a a b b b,求 a a a b b b 之间的所有数字中 0 0 0 ~ 9 9 9 出现次数。

例如, a a a = 1024 1024

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值