数位DP,看这一篇就足够了!

数位DP,看这一篇就足够了!数位 DP 用来解决什么问题 我们有时候会遇到这样一类题目 给你一个区间 l r 找区间上符合某种特定要求的数的个数 这个要求可能很简单 很好理解 但是由于区间范围太大 以至于对每个数进行遍历判别是不太可能的 对于这种情况 就需要用数位 DP 来解决了 朴素解法

数位DP用来解决什么问题?

         我们有时候会遇到这样一类题目,给你一个区间 [l,r] ,找区间上符合某种特定要求的数的个数,这个要求可能很简单,很好理解,但是由于区间范围太大,以至于对每个数进行遍历判别是不太可能的,对于这种情况,就需要用数位DP来解决了。

朴素解法

for(int i = l;i<=r;i++) if(check(i))ans++;

        其中check函数是检验是否满足要求,ans是总个数,这样求解很容易超时,比如 l = 1,r = 1e9,时间上肯定是过不去的,于是我们就要考虑如何优化这个问题了。

两个优化思想

  • 思想1:用f(x)表示不大于x的数中满足要求的个数,这样最后的结果可以表示为:f(r) - f(l-1)
  • 思想2:用一棵树的形式来理解

        这里说的可能有点抽象了,但是没有关系,我们以一道题目为例:给定一个区间 [l.r] ,找到这个区间上所有的“不减数”,不减数定义为从高位到低位数字大小是不减的,比如:223,669,456等等

        那么这道题目我们首先使用第一个思想,假设dp(n)为区间 [0,n] 上的不减数的个数,那么最后的结果应该是dp(r) - dp(l-1),下边我们来详细介绍一下第二个优化思想,我们将数字n各位上的数字从高位到低位排列,如下图所示:

                                    数位DP,看这一篇就足够了!

        我们很容易能够理解,题目中要求的数实际上满足:

                                     数位DP,看这一篇就足够了!

        我们假设:f[i,j] 表示最高位为j,长度为i的数中,不降数的个数,所以我们有代码:

void init() { //长度为1的个数只有一个 for(int i=0;i<=9;i++) f[1][i]=1; //长度大于1时 for(int i=2;i 
  

        下边我们考虑数位,我们将这个数字竖着来写,当我们选择数字时,比如当n=5863时,那么最高位就是5,很明显我们只能取0~5,下边我们将分两种情况,第一种是取0~4,此时只要以0~4开头的所以数是存在的,于是f[0~4][m]就是该部分的不降数个数;对于另一种情况,最高位取了5,那么下一位并不能取遍0~9,所以需要再做一次分类。

               数位DP,看这一篇就足够了!

如下图所示,当第二位取0~7时,那么后边的数字可以取遍0~9,所以满足条件个数为 f[0~7,m-1],对于取8的情况,还要继续分情况,到这里就很清楚的说明为什么数位DP可以用一颗树来形象说明了,中间过程是递归的,下边我给出解题代码:

                数位DP,看这一篇就足够了!

题解

#include 
  
    #include 
   
     #include 
    
      #include 
     
       using namespace std; const int maxn=15; int f[maxn][maxn]; void init() { //长度为1的个数只有一个 for(int i=0;i<=9;i++) f[1][i]=1; //长度大于1时 for(int i=2;i 
      
        nums; while(n) nums.push_back(n%10),n/=10; int res=0;//方案数 int last=0;//保留一些前缀信息,本题是上一个数是几 ///从最大数开始枚举 for(int i=nums.size()-1;i>=0;i--) { int x=nums[i];//x为当前这位数 for(int j=last;j 
       
         =上一位,所以从last开始枚举,最多枚举到x,last为上一位,也即最高位,对下一位的枚举是有限制的 res+=f[i+1][j]; ///左端的节点有i+1个位数(因为第一位的下标是0) if(x 
        
          >l>>r) { cout< 
          
         
        
       
      
     
    
  

不理解模板?再来一道题目

题目链接:https://www.acwing.com/problem/content/1083/

        这里还是解释一下题意:求符合要求的数的个数,要求为一个数字可以被表达为以下形式:

                       数位DP,看这一篇就足够了!

        其中n和a代表一个数字,m1~mp是p个不同的数字,换言之,一个数字n可以写成a的不同幂的和,那么这个数字就是符合要求的。还是给一个区间 [l,r] 求区间中所有符合要求的数字的个数,我们可以将这道题目题解为,将n转换为a进制,该数字中各个位上1个个数正好有K个,我们定义 f[i,j] 代表后i位中包含j个1的数字的个数,初始化过程为:

void init(){ for(int i = 0;i<=N;i++) for(int j = 0;j<=i;j++) if(!j)f[i][j] = 1; else f[i][j] = f[i-1][j]+f[i-1][j-1]; }

我还先将流程放上来:

                    数位DP,看这一篇就足够了!

        如果当前位是0的话,那么该位只能取0,有res += f[i][K - last],last是已经使用了多少个1,如果当前为为1的话,那么last++;如果当前位大于1的话后边的数字将不受限制,res += f[i][K-last-1];break终止即可,下边是详细代码:

题解

#include 
  
    #include 
   
     #include 
    
      using namespace std; int X,Y,K,B; const int N = 35; int f[N][N]; //组合数 //f[i][j]表示i个数中随机先j个的组合数 void init(){ for(int i = 0;i<=N;i++) for(int j = 0;j<=i;j++) if(!j)f[i][j] = 1; else f[i][j] = f[i-1][j]+f[i-1][j-1]; } int dp(int n){ //特判 if(n==0)return 0; //转化为B进制 vector 
     
       nums; while(n){ nums.push_back(n%B); n = n/B; } int res = 0; int last = 0; for(int i = nums.size()-1;i>=0;i--){ int x = nums[i]; //如果x>0 if(x){ //该位取0 res += f[i][K - last]; if(x>1){ //大于1,后边取值不受限制,终止 if(K-last-1>=0)res += f[i][K-last-1]; break; } else { //等于1,用掉一个1 last++; if(last>K)break; } } //特判 if(i==0&&last==K)res++; } return res; } int main(){ init(); cin>>X>>Y>>K>>B; cout< 
       
      
     
    
  

 

           画图码字不易,如果看懂了还请点个赞哦~

                数位DP,看这一篇就足够了!

参考链接

https://www.acwing.com/problem/content/1083/

https://www.acwing.com/solution/content/10735/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224278.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午12:18
下一篇 2026年3月17日 下午12:18


相关推荐

  • 版权文字:Power by DedeCms 如何去除?[通俗易懂]

    版权文字:Power by DedeCms 如何去除?[通俗易懂]dedeCMS系统中的版权声明信息中含有“PowerbyDedeCms”字样,如何去除?dedeCMS近期的新版本至2013-6-7更新包以来,不管新版还是旧版更新补丁包,更新后网站页底都会出现powerbydedecms。*一、powerbydedecms什么意思?在我们上网的时候,会见到页面页底很多带powerbydedecms的网站,powerbydede…

    2022年7月13日
    19
  • 浙文互联AI营销/数字人/AIGC与腾讯元宝(混元大模型底座、微信生态、效率工具

    浙文互联AI营销/数字人/AIGC与腾讯元宝(混元大模型底座、微信生态、效率工具

    2026年3月12日
    2
  • 常用Java编程软件有哪些[通俗易懂]

    常用Java编程软件有哪些[通俗易懂]很多想学Java的人想知道常用的Java编程软件有哪些,毕竟只有掌握软件才能更好的工作。然而,只掌握软件工具并不够,你还需要具备一定的知识基础,更要熟练掌握各个软件的应用,常用的Java编程软件有哪些?1、IntelliJIDEAIntelliJIDEA是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、创新的GUI设计等方面的功能可以说

    2022年7月7日
    50
  • java python oracle推断字符串是否为数字的函数

    java python oracle推断字符串是否为数字的函数

    2022年1月23日
    55
  • python 微信自动回复机器人

    python 微信自动回复机器人python微信自动回复机器人导入wxautohttps://github.com/cluic/wxauto#!python3#-*-coding:utf-8-*-“””Author:tikic@qq.comSource:https://github.com/cluic/wxautoLicense:MITLicenseVersion:3.3.5.3″””fromtokenizeimportNamefromunicodedataimportnameim

    2022年10月1日
    6
  • MySQL外键约束详解

    MySQL外键约束详解今天继续给大家介绍 MySQL 相关知识 本文主要内容是 MySQL 外键约束详解 一 MySQL 外键约束作用二 外键约束创建 一 创建外键约束的条件 二 在创建数据表时创建外键约束 三 在创建数据表后添加外键约束三 外键约束功能演示

    2026年3月26日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号