「数位dp」统计整数数目(力扣第2719题)

这篇具有很好参考价值的文章主要介绍了「数位dp」统计整数数目(力扣第2719题)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本题为1月16日力扣每日一题

题目来源:力扣第2719题

题目tag:数位dp 动态规划

题面

题目描述

给你两个数字字符串num1和num2,以及两个整数max_sum和min_sum。如果一个整数x满足以下条件,我们称它是一个好整数:

\(num1 \leq x \leq num2\)

\(min\_sum \leq digit\_sum(x) \leq max\_sum\)

请你返回好整数的数目。答案可能很大,请返回答案对$ 10^9 + 7 $取余后的结果。

注意,digit_sum(x)表示x各位数字之和。

示例

示例 1

输入:

num1 = "1", num2 = "12", min_num = 1, max_num = 8

输出:

11

解释:

总共有11个整数的数位和在1到8之间,分别是1,2,3,4,5,6,7,8,10,11和12。所以我们返回11。

示例 2

输入:

num1 = "1", num2 = "5", min_num = 1, max_num = 5

输出:

5

解释:

数位和在1到5之间的5个整数分别为1,2,3,4和5。所以我们返回5。

提示

\(1 \leq num1 \leq num2 \leq 10^{22}\)
\(1 \leq min\_sum \leq max\_sum \leq 400\)


思路分析

如果你此前有做过数位dp相关的题目的话,此题一眼就知道应该使用数位dp来完成.此题满足了所有数位dp适用的条件(整数较大,有一个上界和一个正数下界,所求需要数位满足一些性质),所以可以用数位dp来完成.

这里对所求数的范围限制(即 \(num1 \leq x \leq num2\) )可以完全按照数位dp的常规处理方法,用前缀和``差分思想拆成满足 \(x \leq num2\) 的x个数减去满足 \(x \leq num1 - 1\) 的x个数.由于num1为字符串,所以此处的 \(-1\) 操作可用高精度减法来完成.

数位dp有很多种写法(模板,或者叫公式),可以按照个人喜好套用自己擅长的写法,这里我简单介绍一下我自己的写法.

我个人的数位dp分成两个部分,且相对独立,分别如下:

第一部分,给定一个确定的上界,讨论每一位有几种取法.可能你会有疑惑,对于每一位,显然它可以取0到上界这一位的所有数啊?对但不完全对.可以注意到,如果这一位取0到上界这一位-1的数,那么这一位后面的每一位,它都可以任意选择,不受上界限制,此时无需讨论后面的位,可以直接得出答案(答案就是第二部分所求的结果).如果这一位取的是上界这一位的数,那么后续位仍然受到限制,此时需要继续讨论后面的位.因此我们可以发现,对数位的讨论其实是一颗二叉树,左叉是取0到上界这一位-1,这个情况无需继续分叉,可以直接求解;而右叉则是取上界这一位,此时需要继续分叉,不断分小情况求解.这一部分在不同的题目中几乎无需变化,可以完全按一样的写法写.

第二部分,对前面所提到的二叉树的左叉进行直接求解.如何求解?当然是用动态规划来预处理.这部分依据不同的题目采用不同的写法,通常是一个线性dp.比如此题中,题目要求的是各数位之和在一个区间内,那么我们的dp就可以维护成一个前缀和数组.当然在这题中没有用前缀和优化的这个必要,因为时间复杂度完全够.我们就朴素的定义一个三维的状态:

\(dp[i][j][k]\) 表示当前数一共有i位,最高位为j(这两个信息一般是一定有的,因为第一部分中需要使用这两个状态来计算),当前各数位的和为k的数的个数.

这个很好写出状态转移方程,因为当前求和为k,那就是前一位求和为k - j,只需要枚举前一位是几即可,即:

$ dp[i][j][k] = \sum_{l = 0}^{9}dp[i - 1][l][k - j] $

注意累加的结果可能很大,过程中要进行取模.

这两部分分别写好之后,只需要考虑如何在第一部分中具体的直接使用第二部分的结果即可.显然,只需要把当前的位数和最高位直接带进去,然后对在一个范围内的k所对应的dp数组的值进行求和即可.这里的最高位从0枚举到上界这一位-1,分别加到总情况数中.注意到每次循环的进行其实都是在往二叉树的右支走,所以k的这个范围就是将题目给的范围减去上界当前位之前的各位之和即可.当区间不存在时,意味着当前这个右支及以后的所有都不能到达,直接返回即可.如果完整走完了二叉树的所有右支,说明这也是一种合法情况,需要将总情况数加一(前面计算的均为左支,此处是右支,需要单独加).

另外的一些具体细节可以自行看代码.当然,这份代码的时间复杂度并不是最优的(瓶颈主要在第二部分),此处仅作为参考.采用其他的数位dp写法或对此处的dp采用一些技巧进行优化可能能够获得更优的时间复杂度.

参考代码

class Solution {
public:
    long long dp[25][10][410];
    
    // 预处理dp数组(第二部分)
    Solution() {
        memset(dp,0,sizeof(dp));

        // 基线条件,当前只有一位,单独计算
        for(int i = 0;i <= 9;i++) {
            dp[1][i][i] = 1;
        }

        // 线性dp,直接递推即可
        for(int i = 2;i < 25;i++) {
            for(int j = 0;j <= 9;j++) {
                for(int k = j;k < 410 - j;k++) {
                    for(int l = 0;l <= 9;l++) {
                        dp[i][j][k] =(dp[i][j][k] + dp[i - 1][l][k - j]) % 1000000007;
                    }
                }
            }
        }
    }

    int count(string num1, string num2, int min_sum, int max_sum) {
        // 将最高位放在末尾,方便取dp数组中的值
        reverse(num1.begin(),num1.end());
        reverse(num2.begin(),num2.end());

        // 计算给定上界时的数量
        auto cnt = [&](string num) -> long long {
            // 此法中,单独一个0需要特判
            if(num == "0") return 0;

            // 结果
            long long res = 0;
            // 记录走到当前结点前,各数位的和(因为走的右支,所以前面的各数位就是上界中对应位置上的数)
            long long last = 0;

            // 遍历各位
            for(int i = num.size() - 1;i >= 0;i--) {
                int x = num[i] - '0';
                
                // 左支存在时可以直接计算,当前位分别取0到x-1
                for(int k = 0;k < x;k++) {
                    // 当前区间就是要求的区间减掉已经累加的各位数的和
                    for(int j = max(0ll,min_sum - last);j <= max_sum - last;j++) {
                        res = (res + dp[i + 1][k][j]) % 1000000007;
                    }
                }

                // 左支已经直接计算完毕,接下来的递归均为向右支走,累加当前数位
                last += x;
                if(max_sum - last < 0) break; // 不能再往右支走了,结束递推

                // 右支成功走到底,增加这一种情况
                if(!i && last >= min_sum && last <= max_sum) {
                    res = (res + 1) % 1000000007;
                } 
            }
            return res;
        };

        // 高精度减一
        for(auto &c : num1) {
            if(c == '0') {
                c = '9';
            } else {
                c -= 1;
                break;
            }
        }

        // 计算结果,注意取模防负
        return (cnt(num2) - cnt(num1) + 1000000007) % 1000000007;
    }
};

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德文章来源地址https://www.toymoban.com/news/detail-795064.html

到了这里,关于「数位dp」统计整数数目(力扣第2719题)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 力扣刷题第二天 统计整数数目(每天一题)

    统计整数数目 给你两个数字字符串  num1  和  num2  ,以及两个整数  max_sum  和  min_sum  。如果一个整数  x  满足以下条件,我们称它是一个好整数: num1 = x = num2 min_sum = digit_sum(x) = max_sum . 请你返回好整数的数目。答案可能很大,请返回答案对  109 + 7  取余后的结果。 注

    2024年01月16日
    浏览(39)
  • C++ 动态规划 数位统计DP 计数问题

    给定两个整数 a 和 b ,求 a 和 b 之间的所有数字中 0∼9 的出现次数。 例如,a=1024,b=1032 ,则 a 和 b 之间共有 9 个数如下: 1024 1025 1026 1027 1028 1029 1030 1031 1032 其中 0 出现 10 次,1 出现 10 次,2 出现 7 次,3 出现 3 次等等… 输入格式 输入包含多组测试数据。 每组测试数据占一

    2024年02月20日
    浏览(42)
  • acwing算法基础之动态规划--数位统计DP、状态压缩DP、树形DP和记忆化搜索

    暂无。。。 暂无。。。 题目1 :求a~b中数字0、数字1、…、数字9出现的次数。 思路:先计算1~a中每位数字出现的次数,然后计算1~b-1中每位数字出现的次数,两个相减即是最终答案。 那么,如何计算1~a中每位数字出现的次数呢? 首先,将a的每一位存入向量num中,例如a=123

    2024年02月04日
    浏览(51)
  • 力扣每日一题--2088. 统计农场中肥沃金字塔的数目

    看到这道题有些人很容易放弃,其实这道题不是很难,主要是题目长,读的容易让人放弃,但是 只要抓住一些性质就可以解决该问题。     本题中的定义放到图像里其实就是个金字塔,下层的那部分比上一层的那部分,长度加2, 并且该层那个长度区间内都是1才行。是个金

    2024年01月18日
    浏览(52)
  • P2719 搞笑世界杯 (期望dp

    考虑一种票全部卖完,另一种有大于等于2 张的所有情况都为合理情况 dp[i][j]  可以 等概率的转移到 dp[i-1][j] 和 dp[j][i-1]

    2024年02月09日
    浏览(40)
  • dp 就 dp ,数位dp是什么意思 ?

                                                                       💧 dp 就 dp ,数位dp是什么意思 ?💧           🌷 仰望天空,妳我亦是行人.✨ 🦄 个人主页——微风撞见云的博客🎐 🐳 数据结构与算法专栏的文章图文并茂🦕生动形

    2023年04月16日
    浏览(37)
  • NC200204 数位 DP

    题意 传送门 NC200204 dh的帽子 题解 可以独立考虑每一位是否合法。维护数字上下界的状态,枚举三个数字,仅对合法的状态进行转移。数位 DP 求解即可,时间复杂度 O ( l o g R ) O(logR) O ( l o g R ) 。

    2024年02月16日
    浏览(40)
  • 数位dp。

    在处理1e9甚至1e18,1e100的问题时,因为在统计情况下有很多重复的计算,数位dp实现了相同状态只计算一次,从而大幅减少运算时间,思想就是对每一位进行dp,计算时记忆化每一位可以有的状态。 如我们在统计1234的状态时,可以拆成统计0~10000,0~2000,0~300,0~40数位统计 我们

    2024年02月01日
    浏览(36)
  • 数位DP万能模板

    ☆* o(≧▽≦)o *☆嗨~我是小奥🍹 📄📄📄个人博客:小奥的博客 📄📄📄CSDN:个人CSDN 📙📙📙Github:传送门 📅📅📅面经分享(牛客主页):传送门 🍹文章作者技术和水平有限,如果文中出现错误,希望大家多多指正! 📜 如果觉得内容还不错,欢迎点赞收藏关注哟!

    2024年01月17日
    浏览(39)
  • 「学习笔记」数位 DP

    意义不大的题不写了。 点击查看目录 目录 「学习笔记」数位 DP 概述 例题 P2657 [SCOI2009] windy 数 思路 代码 P4317 花神的数论题 思路 P4124 [CQOI2016]手机号码 思路 代码 haha数 题意 思路 代码 0和1的熟练 题意 思路 代码 苍与红的试炼 题意 思路 代码 数位 DP 一般用来解决「在一个较

    2023年04月09日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包