数位dp模板

本文深入探讨了使用递归状态机解决计数问题的方法,特别是在避免特定数字序列时。通过实例讲解了如何利用深度优先搜索(DFS)结合动态规划(DP)进行高效计数,同时介绍了状态传递的巧妙之处,以及如何通过限制条件进行剪枝优化。
// cur : 当前位数
// last: 上一位数对应的数字
// status: 可以当作一个bool值
//          比如题目是不要 4和7,那么只要之前的status = false,那么后面不管如何取值都是 false
// limit : 如果能取0~9就为 false, 否则为 true

// 巧妙的地方有两点
// 1.dfs中的dp记忆条件是当前位数的取值是否受限: 因为一个位数受限的值可能不同,对应的dp也会不同,比如
// 一个上限是 8和一个上限是7的dp肯定会不同,对此要想剪枝,只能让dp取不受限的情况下(上限都是9,幸运的是这种情况占多数)
// 如果不受限dp有值就直接返回值,如果受限的话,只能老老实实做dfs求出对应的值;因为有这种记忆方式,才能让dfs不超时

// 2.statue在dfs中的取值要考虑前一次的情况,比如题目不要4和7,如果上一次的status 是true,那么只要当前位数的
// 取值不是4和7就行了,如果是false,那么我后面不管怎么取值都是false; 直到dfs到最后 cur < 1 的时候返回0或者1
// 以此达到一个计数的目的

ll DIG[20], dp[15][15][5];

ll dfs (int cur, int last, int status, bool limit) {
    if (cur < 1) return status;
    if (!limit && dp[cur][last][status] != -1)
        return dp[cur][last][status];

    int End = limit ? DIG[cur] : 9;
    ll ret = 0;

    for (int i = 0; i <= End; ++i)
        ret += dfs (cur-1, i, status && (i != 4 && i != 7), limit && (i == End)); 

    if (!limit)
        dp[cur][last][status] = ret;
    return ret;
}

ll solve (ll x) { // 先把数的位数用DIG数组存起来
    int m = 0;
    while (x) {
        DIG[++m] = x % 10;
        x /= 10;
    }
    return dfs (m, -1, 1, true); // 第一次dfs是最高位的数,所以limit = true, 对应status根据题意定
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值