iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >如何解决C++数位DP复杂度统计数字问题
  • 806
分享到

如何解决C++数位DP复杂度统计数字问题

2023-06-25 12:06:38 806人浏览 安东尼
摘要

小编给大家分享一下如何解决c++数位DP复杂度统计数字问题,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、问题描述:一本书的页码从自然数 1 开始顺序编码直到自然数 n 。书的页码按照通常的习惯编排, 每个页码不含多余的

小编给大家分享一下如何解决c++数位DP复杂度统计数字问题,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

一、问题描述:

一本书的页码从自然数 1 开始顺序编码直到自然数 n 。书的页码按照通常的习惯编排, 每个页码不含多余的前导数字 0。 例如, 第 6 页用数字 6 表示而不是 06 或 006等。 数字计数问题要求对给定书的总页码 n,计算书的全部页码分别用到多少次数字 0、 1、... 、9。

二、问题分析:

1. 抽取题意:

简单来说就是就是给定一个数字 n,统计 1~n 中用到了多少次数字0~9。

2. 初步思考:

很容易想到要分数位(如个位、十位、百位)来考虑。

为了方便思考,我们做一些约定:

从 0 开始考虑,即考虑0~n中用到了多少次数字0~9,同时计算前导0。

用一个长度为10的数组ans[10] 来存储各个数字的数量。

从最低位开始分析还是最高位开始分析?

应该从最高位开始分析。为什么不先从个位开始考虑呢?因为数字在有除个位外高位(如十位、百位)的情况下,低位是作用于高位的,低位是高位的补充。在这个问题中,单纯的低位并不能说明什么。

3. 示例分析:

① 对于一位数 D 来说,0~D 中用到的数字个数即 0~D 各一次。

② 对于两位数 CD 来说,我们先考虑C,即C0,先不管C有多大,当C可以为0时,有00,01, 02, ... , 09 这样的数字,则我们知道,C在为0的时候个位0~9的数字各用了一次,同时C本身被用到了10次;当C为1时,有这样的数字10,11,12, ..., 19 ,我们知道 C 为1的时候个位0~9的数字各用了一次,同时C 被用了10次。

以此类推我们知道,C每大一,个位数字0~9就都会被用到1 次。并且本身作为十位的C,C每大一,当前C表示的数字就会被用到10次。

注意此时C不能为最大值,因为C为最大值的话按照上述方法考虑,可能超过CD本来的值。

再来考虑D,

当C为最大值的时候,用到的数字即C0,C1,...,CD ,由此我们知道C为最大值时C会被用D+1次,0~D各用了一次。

经过总结得到,在考虑前导0的情况下,一个十位数字CD 0~9的数字用到的情况如下:

考虑十位得到的 
ans [ 0~9 ] + 1*C  (虽然此时不能考虑十位最大值,但是有0啊) 
ans [ 0~C-1 ] + 10^1   
ans [C] + (D+1)  (此时十位取最大值)
 
考虑个位得到的
ans[ 0~D ] + 1

③ 有了上面的经验,我们来考虑三位数 ABC

首先对于百位A,即A00, A01, ... , A99

我们来统计一下个位和十位上的数字即 00,01,...,99加起来一共用了多少次0~9

按照上面的想法我们很容易知道,十位从0开始每加一,个位0~9就都会被用 1 次,同时十位本身,会被用到10次。这样我们知道每个数字都被用了 10*1 + 10 次,即20次。

同理并根据上面的结论,对于 000,001,...,999 ,我们知道,如果百位从0开始每加一,个位和十位的数字组合在一次0~9各会被用到 20次,同时作为百位本身,会被用到100次。这样我们知道每个数字都被用了 10*20 + 10^2 次,即300次。

根据这个思想很容易发现规律,对于n 位的数字 0 到 n 位的数字9,设 0~9 各数字会被用到的次数为F(n),则有 F(n) = 10 * F(n-1) + 10^(n-1),其中F(1)= 1

我们把结果记入一个名为dp 的数组:dp[0] = 0, dp[1]=1, dp[2]=20, dp[3]=300 , ....

这样我们可以知道

仅考虑百位A,有 
ans [ 0~9 ] + 20*A 
ans [0~A-1] + 10^2 
ans [ A ] + (BC + 1)
 
仅考虑十位,有
ans[ 0~9] + 1*B 
ans[ 0~B-1] + 10^1 
ans[ B ] + (C+1)
 
仅考虑个位 
ans[ 0~C ] +1
 

4. 总结规律:

总结上方的规律可知,

在计入前导0的情况下,要考虑0~n 的数用到了多少各0~9的数字,应该自高向低逐次取出每一位分析

设取出的一位为数字为 X,同时其位于从个位数起的第 Y 位,剩余的低位构成的数字为 Z ,

则答案计算方法为:

ans[0~9] + dp[Y-1]*X   (当X < max 时考虑低位) ans[0~X-1] + 10^(Y-1)  (当X < max 时考虑X) ans[X] + (Z+1)    (当 X = max 时考虑 X)

经检查,该方法适用于个位及特殊情况。

5. 解除约定:

我们来考虑如何除去前导0

首先要明白一件事,前导0只对0的计数产生影响。

那么前导0是在哪里产生的呢?

如果上面说的你都明白了,那么很容易知道就在计算最高位时的这两步

ans[0~9] + dp[Y-1]*X   (当X < max 时考虑低位)
ans[0~X-1] + 10^(Y-1)  (当X < max 时考虑X)

在最高位为0的时候多余出来的

按照上述方法考虑,设 n 位数多余出来的0的个数为 G(n)

① 你可以想象把所有数字都右对齐,得到:

G (1) = 0

G (2) = 10

G (3) = 10 + 100

......

G (n) = 10^1 + 10^2 + ... + 10^(n-1) ,其中 n>2

② 或者这样想:

两位数可以对所有个位数在十位补0,三位数可以在两位数的基础上再在百位上对所有两位数再补一个0,以此类推,易知这也是一个dp

得到 G (n) = G(n-1) + 10^(n-1),其中 G(1) = 0,n>2

至此,思考部分完成。

三、 编写代码:

算法时间复杂度仅为 O ( log10(n) ) ,接近 O (1) ,吊打暴力 O (nlog10n) 的算法。

#include <bits/stdc++.h> using namespace std; typedef long long LL; LL n;LL dp[20], ans[20],zeroNum[20];  //zeroNum 用于记录 n 位数的前导 0 个数LL pow10[20];  //pow10 用于记录 10 的次方数void makeDp(){    pow10[0]=1;    for(int i=1;i<15;i++) pow10[i] = pow10[i-1]*10;    dp[0]=0,dp[1]=1;    zeroNum[0]=zeroNum[1]=0;    for(int i=2;i<15;i++){        pow10[i]=pow10[i-1]*10;        zeroNum[i]=zeroNum[i-1] + pow10[i-1];        dp[i]=10*dp[i-1] + pow10[i-1];    } } void solve(LL n,LL ans[]){    if(n==0){        ans[0]=1;        return;    }    LL bitNum = log10(n) + 1;    LL num[20];    LL nTmp = n;    for(int i=1;i<=bitNum;i++){        num[i] = nTmp%10;        nTmp/=10;    }    for(int i=bitNum;i>=1;i--){        LL x = num[i];        LL y = i;        n -= x*pow10[y-1];        LL z = n;        for(int j=0;j<10;j++){            ans[j] += dp[y-1]*x;        }        for(int j=0;j<x;j++){            ans[j]+=pow10[y-1];        }        ans[x] += z+1;    }    ans[0]-=zeroNum[bitNum];} int main(){    cin>>n;      makeDp();       solve(n,ans);    for(int i=0;i<10;i++){        if(i==0) printf("%lld\n", ans[i]-1);        else printf("%lld\n",ans[i]);    }}

四、 相关例题:

洛谷P2602:https://www.luogu.com.cn/problem/P2602

题目描述

给定两个正整数 aa 和 bb,求在 [a,b][a,b] 中的所有整数中,每个数码(digit)各出现了多少次。

输入格式

仅包含一行两个整数 a,ba,b,含义如上所述。

输出格式

包含一行十个整数,分别表示 0\sim 90∼9 在 [a,b][a,b] 中出现了多少次。

输入输出样例

输入

1  99

输出

9 20 20 20 20 20 20 20 20 20

说明/提示

数据规模与约定

对于 30% 的数据,保证 a≤ b ≤ 10^6;

对于 100% 的数据,保证 1≤a≤b≤10^12。

改改代码直接交:

#include <bits/stdc++.h>using namespace std; typedef long long LL; LL a,b;LL dp[20], ans1[20],ans2[20],zeroNum[20];LL pow10[20]; void makeDp(){    pow10[0]=1;    for(int i=1;i<15;i++) pow10[i] = pow10[i-1]*10;    dp[0]=0,dp[1]=1;    zeroNum[0]=zeroNum[1]=0;    for(int i=2;i<15;i++){        pow10[i]=pow10[i-1]*10;        zeroNum[i]=zeroNum[i-1] + pow10[i-1];        dp[i]=10*dp[i-1] + pow10[i-1];    } } void solve(LL n,LL ans[]){    if(n==0){        ans[0]=1;        return;    }    LL bitNum = log10(n) + 1;    LL num[20];    LL nTmp = n;    for(int i=1;i<=bitNum;i++){        num[i] = nTmp%10;        nTmp/=10;    }    for(int i=bitNum;i>=1;i--){        LL x = num[i];        LL y = i;        n -= x*pow10[y-1];        LL z = n;        for(int j=0;j<10;j++){            ans[j] += dp[y-1]*x;        }        for(int j=0;j<x;j++){            ans[j]+=pow10[y-1];        }        ans[x] += z+1;    }    ans[0]-=zeroNum[bitNum];} int main(){    cin>>a>>b;    makeDp();    solve(a-1,ans1);    solve(b,ans2);    for(int i=0;i<10;i++){        printf("%lld ",ans2[i]-ans1[i]);    }}

看完了这篇文章,相信你对“如何解决C++数位DP复杂度统计数字问题”有了一定的了解,如果想了解更多相关知识,欢迎关注编程网其他教程频道,感谢各位的阅读!

--结束END--

本文标题: 如何解决C++数位DP复杂度统计数字问题

本文链接: http://www.lsjlt.com/news/304606.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何解决C++数位DP复杂度统计数字问题
    小编给大家分享一下如何解决C++数位DP复杂度统计数字问题,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、问题描述:一本书的页码从自然数 1 开始顺序编码直到自然数 n 。书的页码按照通常的习惯编排, 每个页码不含多余的...
    99+
    2023-06-25
  • C++数位DP复杂度统计数字问题示例详解
    目录一、问题描述:二、问题分析:1. 抽取题意:2. 初步思考:3. 示例分析:4. 总结规律:5. 解除约定:三、 编写代码:四、 相关例题:Tips:如果你是真的不理解,不要只看...
    99+
    2024-04-02
  • C/C++题解LeetCode1295统计位数为偶数的数字
    目录题目描述思路分析AC 代码将int转为String代码3种方法 - 统计位数为偶数的数字题目描述 1295. 统计位数为偶数的数字 - 力扣(LeetCode) 给你一个整数...
    99+
    2023-01-03
    C/C++统计位数为偶数字 C/C++ LeetCode
  • Java C++题解leetcode902最大为N的数字组合数位DP
    目录题目要求阅读理解思路:数位DPJavaC++总结题目要求 题目链接 阅读理解 思路:数位DP Java class Solution { public int a...
    99+
    2022-11-13
    Java C++ 最大为N的数字组合 Java C++ 数位DP
  • PHP 函数中如何处理时间复杂度问题?
    时间复杂度是衡量函数执行时间的指标。常见的 php 函数时间复杂度问题包括循环嵌套、大量数组遍历和递归调用。优化时间复杂度的技术包括:使用缓存减少循环次数简化算法使用并行处理 如何在 ...
    99+
    2024-04-26
    php 时间复杂度
  • C语言数学问题与简单DP背包问题怎么解决
    本篇内容介绍了“C语言数学问题与简单DP背包问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!数学顾名思义,数学类的题就是都可以用数...
    99+
    2023-06-30
  • JS如何解决超出精度数字的问题
    这篇文章主要为大家展示了“JS如何解决超出精度数字的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JS如何解决超出精度数字的问题”这篇文章吧。精度问题最通俗易懂的解释比如一个数 1÷3=0....
    99+
    2023-06-20
  • 如何解决js数字计算误差的问题
    这篇文章主要为大家展示了“如何解决js数字计算误差的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决js数字计算误差的问题”这篇文章吧。实例如下://...
    99+
    2024-04-02
  • C语言怎么解决无重复数字问题
    这篇文章主要介绍了C语言怎么解决无重复数字问题的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C语言怎么解决无重复数字问题文章都会有所收获,下面我们一起来看看吧。题目:有1、2、3、4个数字,能组成多少个互不相同...
    99+
    2023-06-17
  • Python 数据结构的魔力:解决复杂问题
    ...
    99+
    2024-04-02
  • PHP开发中如何使用设计模式解决复杂问题
    引言:在PHP开发中遇到复杂问题时,我们通常会使用设计模式来解决。设计模式是一套被广泛接受的解决方案,可用于解决各种软件开发中的常见问题。本文将介绍一些常用的设计模式,并提供相应的代码示例,以帮助读者更好地理解和应用这些设计模式。一、单例模...
    99+
    2023-10-21
    设计模式 PHP开发 解决复杂问题
  • 如何利用Brainstorm框架解决复杂问题
    使用Brainstorm框架解决复杂问题的步骤如下: 定义问题:明确问题的核心,并确保所有团队成员对问题的理解一致。 收集信...
    99+
    2024-03-08
    Brainstorm
  • C++ 函数优化详解:如何优化时间复杂度?
    为了优化 c++++ 函数的时间复杂度,可以通过以下方法:①避免不必要的复制操作;②减少函数调用;③使用高效的数据结构。举例来说,采用备忘录技术可以将斐波那契数列计算的复杂度从 o(2^...
    99+
    2024-05-03
    c++ 函数优化
  • C++ 函数优化详解:如何优化空间复杂度?
    减少 c++++ 函数的空间复杂度可通过以下技巧:使用智能指针、传递引用而非复制、使用常量引用、传递值而非指针、优化容器大小。通过使用智能指针、传递 token 所有权等实战技巧,可以减...
    99+
    2024-05-04
    c++ 函数优化 内存占用
  • 如何解决复杂系统中的用户权限数据库设计
    本篇内容主要讲解“如何解决复杂系统中的用户权限数据库设计”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何解决复杂系统中的用户权限数据库设计”吧!需求陈述 不同...
    99+
    2024-04-02
  • Brainstorm框架如何帮助解决复杂问题
    定义问题: Brainstorm框架可以帮助团队清晰地定义复杂问题,并确定问题的关键因素和挑战。 生成创意: Brainst...
    99+
    2024-03-14
    Brainstorm
  • ASP和Laravel开发中,NumPy如何帮助我们解决复杂的数据计算问题?
    ASP和Laravel是两个非常流行的Web开发框架,它们都提供了强大的工具和功能,帮助开发者快速构建高质量的Web应用。但是,在处理大量数据的时候,这些框架可能会遇到一些困难。这时候,NumPy就可以发挥重要作用了。本文将介绍NumPy在...
    99+
    2023-06-22
    laravel 并发 numy
  • 如何解析Java 数据结构中时间复杂度与空间复杂度
    这篇文章给大家介绍如何解析Java 数据结构中时间复杂度与空间复杂度,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。算法效率在使用当中,算法效率分为两种,一是时间效率(时间复杂度),二是空间效率(空间复杂度)。时间复杂度...
    99+
    2023-06-25
  • python中小数点后的位数问题如何解决
    python中小数点后的位数第一种方法a = 8.8888使用round 函数b = round(a,2) # 保留小数点后两位小数,会四舍五入 b 就等于8.89第二种方法b= "%.2f"%a # 也会四舍五入第三种...
    99+
    2023-05-15
    Python
  • LeetCode和NumPy:如何解决复杂的编程问题?
    在现代计算机科学和数据科学中,编程问题越来越复杂。为了解决这些问题,开发者需要掌握一些强大的工具和技术。LeetCode和NumPy就是两个非常有用的工具,它们可以让开发者更快速地解决复杂的编程问题。 LeetCode是一个流行的算法题目...
    99+
    2023-10-17
    leetcode 框架 numy
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作