【题型总结】数位dp(一)

简介: 【题型总结】数位dp(一)

数位动态规划

数位DP介绍

  • 可以解决的问题
    对于「数位 DP」题,都存在「询问[a, b](a 和 b 均为正整数,且 a符合特定条件的数值个数为多少」的一般形式,通常我们需要实现一个查询 [0,x] 有多少合法数值的函数 f(int x),然后应用「容斥原理」求解出 [a,b] 的个数:f(b)−f(a−1)
  • 这个区间可能很大,暴力代码一般会超时,此时就可以使用数位DP解决该类问题。
  • 由于数位是按位dp,数的大小对复杂度的影响很小,时间复杂度为状态个数 * 单个状态的转移次数
  • 模板题

试计算在区间 1到 n的所有整数中,数字 x(0≤x≤9)共出现了多少次?例如,在 1 到 11 中,即在 1,2,3,4,5,6,7,8,9,10,11 中,数字 1出现了 4次。

模板

image.png

作者:灵茶山艾府

链接:https://leetcode.cn/problems/number-of-digit-one/solutions/1750339/by-endlesscheng-h9ua/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关题目

1.数字1的个数【LC233】

2022/11/25

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

image.png

实现

  • 首先将n转化为字符串数组s,存储高位至低位的数值大小,会影响isLimit
  • 然后使用dp数组记录在不受到约束时,第i位枚举值固定时的合法方案数,即从左往右第i位之后所有枚举数中1的个数【不包括第i位】
  • 然后通过for循环,枚举每一位其所有可能的值
  • 举例说明:计算所有小于等于 n 的非负整数中数字 1 出现的个数。

如果当前的枚举数不受限,并且状态dp[i,cnt1]之前已经计算过,那么此时第i位枚举值固定时的合法方案数不需要重新计算,直接返回dp[i,cnt1]即可

class Solution {
    char s[];
    int dp[][];
    public int countDigitOne(int n) {
        s = Integer.toString(n).toCharArray();
        int m = s.length;
        dp = new int[m][m];
        for (int i = 0; i < m; i++) Arrays.fill(dp[i], -1);
        return f(0, 0, true);// 第i位受isLimit约束
    }
    int f(int i, int cnt1, boolean isLimit) {
        if (i == s.length) return cnt1;
        if (!isLimit && dp[i][cnt1] >= 0) return dp[i][cnt1];
        int res = 0; // 记录从左往右第i+1位之后所有枚举数中1的个数
        for (int d = 0, up = isLimit ? s[i] - '0' : 9; d <= up; ++d)
            // d代表枚举要填入的数字 up代表上限值 不受限则为9 受限则为第i位的值
            // 以下代码运用到了回溯
            // 具体含义为:寻找从左往右第i+1为以及之后数位中1的个数,因此需要更新cnt1以及isLimit
            // cnt1含义为前面i位填充1的个数,因此如果当前位填入的数字为i,那么cnt1 + 1,否则加0
            // isLimit含义为该位填充数字是否受限,取决于前一次填充是否受限,以及当前填充是否达到up
            res += f(i + 1, cnt1 + (d == 1 ? 1 : 0), isLimit && d == up);
        if (!isLimit) dp[i][cnt1] = res;// dp数组的含义为在不受到约束时第i位的合法方案数,即从左往右第i位之后所有枚举数中1的个数【不包括第i位】
        return res;
    }
}

复杂度

  • 时间复杂度:O(logn),$\lceil log_{10}n \rceil+1$为数字的位数,时间复杂度 = 状态个数 * 单个状态的转移次数,状态个数就是 dp 数组的长度,即O(logn),而单个状态的转移次数 = O(2) = O(1),所以时间复杂度为O(logn)
  • 空间复杂度:O(logn)

2.不含连续1的非负整数【LC600】

Given a positive integer n, return the number of the integers in the range [0, n] whose binary representations do not contain consecutive ones.

2022/11/25

LC233的思路相同,区别在于把数使用二进制表示

  • 思路:数位dp
    首先将n转换成二进制int数组binary,定义f(i,pre,isLimit,isNum)表示构造从左往右第i位及其之后数位的合法方案数,即不存在连续1的个数,其余参数的含义为:

image.png

实现

  • 首先将n转化为二进制形式,并使用int数组binary存储
  • 然后使用dp[i][j]数组代表在不受到约束时,第i位枚举值固定为j时的,合法方案数,即从左往右第i位之后所有枚举数中不含1的个数【不包括第i位】
  • 然后对于每一位枚举其所有可能的值,由于枚举值只可能为0或者1,因此可以不使用for循环,直接判定
  • 如果不受限并且前一位不为1并且最大值为0,那么第i位可以填充1
  • 任何情况均可以填充0
  • 代码
class Solution {
    int [] binary;
    int[][] dp;
    public int findIntegers(int n) {
        int m = 32;        
        binary = new int[m];// 转化为二进制数组
        for (int i = 0; i < m; i++){
            binary[i] = (n >> (31-i)) & 1;
        }        
        dp = new int[m][2];
        for (int i = 0; i < m; i++){
            Arrays.fill(dp[i],-1);
        }
        int i = 0;
        while (i < m && binary[i] == 0){// 找到第一个不为0的二进制
            i++;
        }
        return f(i,0,true);
    }
    public int f(int i, int pre, boolean isLimit){
        if (i == binary.length){
            return 1;
        }
        if (!isLimit && dp[i][pre] >= 0){// 不受限 并以及计算过
            return dp[i][pre];
        }
        int res = 0;// 记录第i位之后的枚举可能性
        int up = isLimit ? binary[i] : 1;// 
        res += f(i + 1, 0, isLimit && up == 0);// 第i+1位填0 
        if (pre != 1 && up == 1){
            res += f(i + 1, 1, isLimit);// 第i位填1
        }               
        if (!isLimit){
            dp[i][pre] = res;
        }
        return res;
    }    
}

image.png

【题型总结】数位dp(二):https://developer.aliyun.com/article/1405899?spm=a2c6h.13148508.setting.14.45fd4f0eFyVSCn

目录
相关文章
|
算法
蓝桥杯算法竞赛第一周题型总结
蓝桥杯算法竞赛第一周题型总结
78 0
|
2月前
lanqiao oj 17136 星球(状态压缩dp)
lanqiao oj 17136 星球(状态压缩dp)
14 0
蓝桥杯:最大公约数 2020省赛 例题:既约分数
蓝桥杯:最大公约数 2020省赛 例题:既约分数
71 0
|
7月前
|
C语言
PTA 浙大版《C语言程序设计(第3版)》题目集 习题8-4 报数 (20分)
PTA 浙大版《C语言程序设计(第3版)》题目集 习题8-4 报数 (20分)
[算法刷题题解笔记] 洛谷 P1011 [NOIP1998 提高组] 车站 [数学|斐波那契|推导]
[算法刷题题解笔记] 洛谷 P1011 [NOIP1998 提高组] 车站 [数学|斐波那契|推导]
|
7月前
蓝桥备战--分糖果OJ2928 贪心 分类讨论
蓝桥备战--分糖果OJ2928 贪心 分类讨论
72 0
|
7月前
|
存储 网络架构
【题型总结】数位dp(二)
【题型总结】数位dp(二)
81 0
|
7月前
|
机器学习/深度学习
蓝桥杯-2/14天-完全平方数【另类思路】
蓝桥杯-2/14天-完全平方数【另类思路】
|
Python
牛客刷题之数学基础-约数
牛客刷题之数学基础-约数
62 0
|
算法
算法 - 蓝桥杯并查集题型
算法 - 蓝桥杯并查集题型
109 0