由于水平原因,博客大部分内容摘抄于网络,如有错误或者侵权请指出,本人将尽快修改

数位DP

给一个区间[A,B],求在A,B之间满足条件的数字的个数。解题思路,使用递归每位数字变化,求满足的个数。

模板1.0 不考虑下界

一、不考虑前导 0 的模板

以下是不考虑前导 0 的模板代码:

    static int dfs(int pos, boolean limit, String str, int[] dp) {
        if (pos == str.length()) {
            return 1;
        }
        //没有限制本位填写的数字
        if (dp[pos]!= -1 &&!limit) {
            return dp[pos];
        }
        int res = 0;
        int up = limit? str.charAt(pos) - '0' : 9;
        for (int d = 0; d <= up; d++) {
            res += dfs(pos + 1, limit && d == up, str, dp);
        }
        //没有限制本位填写的数字
        if (!limit) {
            dp[pos] = res;
        }
        return res;
    }

二、考虑前导 0 的模板

考虑前导 0 的模板代码如下:

    static int dfs(int pos, boolean limit, boolean isNum, String str, int[] dp) {
        if (pos == str.length()) {
            return isNum? 1 : 0;
        }
        if (dp[pos]!= -1 &&!limit) {
            return dp[pos];
        }
        int res = 0;
        //如果右前导0,当前位也可以继续填写0,即跳过
        if (!isNum && str.charAt(pos) == '0') {
            res += dfs(pos + 1, false, false, str, dp);
        }
        //往上一行不是return,会继续往下走,所以要判断isNum,前面填写了前导0,本位可以填写数字
        int start = isNum? 0 : 1;
        int up = limit? str.charAt(pos) - '0' : 9;
        for (int d = start; d <= up; d++) {
            res += dfs(pos + 1, limit && d == up,  true, str, dp);
        }
        if (!limit ) {
            dp[pos]= res;
        }
        return res;
    }

模板2.0 考虑下界

一、不考虑前导0

二、考虑前导0


三、数位概念

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

例如在十进制数中,每一位数字的取值范围是 0 - 9,其他进制数可类比十进制。比如二进制,每一位数字的取值范围就是 0 - 1。在 leetcode 中目前的题目大多是基于十进制和二进制的。

四、数位 DP(题目应用特征和条件)

  1. 计数:数位 DP 常用于计数问题。
  2. 输入限制:输入会提供一个数字区间(或者只提供上界)作为计数的限制条件。
  3. 转换理解:题目条件经过转换后可以使用「数位」的思想去判断和理解。

五、为什么可以用数位 DP

  1. 朴素计数的不足:最朴素的计数方法是从小到大依次加一。
  2. 重复计算问题:但对于位数比较多的数,这样的计数过程中有许多重复的部分。例如从 700 数到 799、从 800 数到 899、和从 900 数到 999 的过程非常相似,它们都是后两位从 00 变到 99,不一样的地方只有百位这一位。
  3. 避免重复计算的方法:我们可以把重复计算的过程归并保证不重复计算,即将这些过程中产生的计数答案存放在一个数组里。此数组根据题目具体要求设置状态,用递推或 DP 的方式进行状态转移。
  4. 统计答案的方式:统计答案建议使用记忆化搜索,为了统计所有不超过上限的答案,要从高到低枚举每一位,再考虑每一位都可以填哪些数字,最后利用通用答案数组统计答案。

六、解题方法

  1. 相对固定参数设置
    • 假设 n 是范围的最大值。
    • pos(必选):表示第 pos 个数位。
    • islimit(必选):表示当前是否受到了 n 的约束。若为真,则第 i 位填入的数字至多为 s[i],否则至多为 9。例如 n = 234,如果前面填了 23,那么最后一位至多填 4;如果前面填的不是 23,那么最后一位至多填 9。如果在受到约束的情况下填了 s[i],那么后续填入的数字仍会受到 n 的约束。
    • isnum(可选):表示 i 前面的数位是否填了数字。若为假,则当前位可以跳过(不填数字),或者要填入的数字至少为 1;若为真,则要填入的数字可以从 0 开始。例如 n = 123,在 i = 0 时跳过的话,相当于后面要构造的是一个 99 以内的数字了,如果 i = 1 不跳过,那么相当于构造一个 10 到 99 的两位数,如果 i = 1 跳过,相当于构造的是一个 9 以内的数字。
    • mask(可选):表示前面选过的数字集合,换句话说,第 i 位要选的数字不能在 mask 中。
posted @ 2024-11-03 17:04  小纸条  阅读(80)  评论(0)    收藏  举报