最基本的一类数位dp题,题目大意一般是在a~b的范围,满足某些要求的数字有多少个,而这些要求一般都是要包含或者不包含某些数字,或者一些带着数字性质的要求,一般来说暴力是可以解决这一类问题,可是当范围非常大时,暴力明显会超时,这时便是需要把它转化为一类dp问题,这就是数位dp。像一个数1234567890,直接把它暴力的话,那它就是一个1e9级别的数,可是把它一个数位一个数位的话,那它就是1位是0,2为是9,一直到10位是1,一共才10位的长度,明显就比暴力明朗多了。

  由于我也是刚接触不久,还没有什么自己深刻感悟,日后再做总结,现在先具体问题具体分析,来两道入门的数位dp题进行进行理解

不要62和4HDU - 2089

  题目大意就是,一个数字它如果含有62或者4它就是不吉利数字,而你要求的就是【n,m】范围内的不含有不吉利数字的个数。

  这题的范围只有1e6,所以暴力还是行得通的,不过我们在学数位dp,就用数位dp来解决。那我们想一下,每一个数个位,十位,百位。。。的范围都是从0~9,那么我们把一个数拆成一位位,例如,一个万位的数就可以拆成

万:0 1 2 3 4 5 6 7 8 9

千:0 1 2 3 4 5 6 7 8 9

百:0 1 2 3 4 5 6 7 8 9

十:0 1 2 3 4 5 6 7 8 9

个:0 1 2 3 4 5 6 7 8 9

  现在假如一个数的10086,那它的范围内有多少个不含不吉利吉利的数呢,首先我们得把10086分解,得出一个上界,类似这个样子

万:0 1 2 3 4 5 6 7 8 9 上界1

千:0 1 2 3 4 5 6 7 8 9 上界0

百:0 1 2 3 4 5 6 7 8 9 上界0

十:0 1 2 3 4 5 6 7 8 9 上界8

个:0 1 2 3 4 5 6 7 8 9 上界6

  这个上界就是在有限制的情况下,当前数位的数字选择只能从[0,上界]中选择,那么这个限制又是什么吗,其实就是范围限制,就像现在我们要求的是10086范围内的,那么如果万位取0,对千位肯定就没有限制可以取[0,9]中任意的数字,但当万位取1,千位就有限制了只能取到0,要是取到1那么就不是求10086范围内的了,而是求11xxx范围内的了,那这个限制怎么传递呢,首先当前数位如果没有限制的话,那么对下面的数位也没有限制,像万位取0,下面的数位都没有限制,而当前数位如果有限制的话,并且取的是上界的话,对下面的数位就有限制,像万位取1,千位有限制,然后千位取0是上界又对下面的又有限制,而像万位取1,千位取0,百位取0,对十位有限制,但十位取小于8的数对个位就没有限制,所以上界的限制传递的条件就是,当前数位有限制并且取的是上界,就会对下面的数位有限制

  那现在回到问题,怎么确定不是不吉利的数有几个呢,首先每一位肯定是不能含有4这个数字的,然后就是上一数位是6,当前数位就不能是2,详情见代码

  1. #include<cstdio>
  2. #include<cstring>
  3. int n,m,up[],dp[][];//dp[i][j]数位i的上一数字是不是6时不含不吉利的数字有多少个
  4. int dfs(int p,bool is6,bool isu)//p当前数位,is6上一个数位是不是6,isu当前数位有没有限制
  5. {
  6. if(p==)//边缘值,p为0是个空集,肯定没有62或4,返回1
  7. return ;
  8. if(!isu&&dp[p][is6]!=-)
  9. return dp[p][is6];
  10. int ans=;
  11. for(int i=;i<=(isu ? up[p] : );i++)
  12. {
  13. if((is6&&i==)||i==)//当前数位不能取4,以及如果上一数位取6,当前数位不能取9
  14. continue;
  15. ans+=dfs(p-,i==,isu&&i==up[p]);//继续往下判断下一数位
  16. }
  17. if(!isu)
  18. dp[p][is6]=ans;
  19. return ans;
  20. }
  21. int solve(int x)
  22. {
  23. int num=;
  24. while(x)
  25. {
  26. up[++num]=x%;
  27. x/=;
  28. }
  29. return dfs(num,,);
  30. }
  31. int main()
  32. {
  33. memset(dp,-,sizeof(dp));
  34. while(scanf("%d%d",&n,&m)&&(n||m))
  35. printf("%d\n",solve(m)-solve(n-));
  36. return ;
  37. }

666

  关于记忆化,当前数位没有限制时,那么它取任何数相对应的结果是通用的,记忆化搜索就达到了省时的效果

要49HDU - 3555

  相比上一题不要62和4,这题就是给出一个n,要求[1,n]范围内含49的数字有多少个,不过我们可以反过来求出不含49的,然后减去即可

  1. #include<cstdio>
  2. #include<cstring>
  3. #define ll long long
  4. ll n,dp[][];
  5. int t,up[];
  6. ll dfs(int p,bool is4,bool isu)
  7. {
  8. if(p==)
  9. return ;
  10. if(!isu&&dp[p][is4]!=-)
  11. return dp[p][is4];
  12. ll ans=;
  13. for(int i=;i<=(isu ? up[p] : );i++)
  14. {
  15. if(is4&&i==)
  16. continue;
  17. ans+=dfs(p-,i==,isu&&i==up[p]);
  18. }
  19. if(!isu)
  20. dp[p][is4]=ans;
  21. return ans;
  22. }
  23. ll solve(ll x)
  24. {
  25. int num=;
  26. while(x)
  27. {
  28. up[++num]=x%;
  29. x/=;
  30. }
  31. return dfs(num,,);
  32. }
  33. int main()
  34. {
  35. scanf("%d",&t);
  36. memset(dp,-,sizeof(dp));
  37. while(t--)
  38. {
  39. scanf("%lld",&n);
  40. printf("%lld\n",n+-solve(n));
  41. }
  42. return ;
  43. }

999

  对于答案的+1,因为我们的数位dp求的是[0,n]范围内的不含49的数字的个数,而题目要求的是[1,n]范围内含49的数字的个数,直接用n减去的话,0肯定不含49,会多减去一个0,所以要+1

当然也有直接就是求[1,n]范围内含49的数字个数的,详情见代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #define ll long long
  4. ll n,dp[][],cf[]={};//cf保存10的次方
  5. int t,up[];
  6. ll dfs(int p,bool is4,bool isu)
  7. {
  8. if(p==)//空集不含49,返回0
  9. return ;
  10. if(!isu&&dp[p][is4]!=-)
  11. return dp[p][is4];
  12. ll ans=;
  13. for(int i=;i<=(isu ? up[p] : );i++)
  14. {
  15. if(is4&&i==)//上一数位的4,当前位是9,可以计算答案了
  16. ans+=isu ? n%cf[p-]+ : cf[p-];//有上界的限制,比如n是49870,
  17. //当前位是第4位取9,那么49000~49870都是可以的,也就是n%1000+1
  18. //反之没有限制的话,比如n是50000,当第5位是4,当前位是第4位取9
  19. //那么49000~49999都是可以的,也就是1000
  20. else
  21. ans+=dfs(p-,i==,isu&&i==up[p]);
  22. }
  23. if(!isu)
  24. dp[p][is4]=ans;
  25. return ans;
  26. }
  27. ll solve(ll x)
  28. {
  29. int num=;
  30. while(x)
  31. {
  32. up[++num]=x%;
  33. x/=;
  34. }
  35. return dfs(num,,);
  36. }
  37. int main()
  38. {
  39. for(ll i=;i<=;i++)
  40. cf[i]=cf[i-]*;
  41. scanf("%d",&t);
  42. memset(dp,-,sizeof(dp));
  43. while(t--)
  44. {
  45. scanf("%lld",&n);
  46. printf("%lld\n",solve(n));
  47. }
  48. return ;
  49. }

999

数位dp入门 HDU 2089 HDU 3555的更多相关文章

  1. HDU 2089 不要62【数位DP入门题】

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. hdu3555 Bomb 数位DP入门

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 简单的数位DP入门题目 思路和hdu2089基本一样 直接贴代码了,代码里有详细的注释 代码: ...

  3. xbz分组题B 吉利数字 数位dp入门

    B吉利数字时限:1s [题目描述]算卦大湿biboyouyun最近得出一个神奇的结论,如果一个数字,它的各个数位相加能够被10整除,则称它为吉利数.现在叫你计算某个区间内有多少个吉利数字. [输入]第 ...

  4. 数位dp入门 hdu2089 不要62

    数位dp入门 hdu2089 不要62 题意: 给定一个区间[n,m] (0< n ≤ m<1000000),找出不含4和'62'的数的个数 (ps:开始以为直接暴力可以..貌似可以,但是 ...

  5. 【数位dp】【HDU 3555】【HDU 2089】数位DP入门题

    [HDU  3555]原题直通车: 代码: // 31MS 900K 909 B G++ #include<iostream> #include<cstdio> #includ ...

  6. 递推、数位DP解析(以HDU 2089 和 HDU 3555 为例)

    HDU 2089 不要62 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2089 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人 ...

  7. HDU 2089 - 不要62 - [数位DP][入门题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 Time Limit: 1000/1000 MS (Java/Others) Memory Li ...

  8. Hdu 2089 不要62 (数位dp入门题目)

    题目链接: Hdu 2089 不要62 题目描述: 给一个区间 [L, R] ,问区间内不含有4和62的数字有多少个? 解题思路: 以前也做过这个题目,但是空间复杂度是n.如果数据范围太大就GG了.今 ...

  9. hdu:2089 ( 数位dp入门+模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 数位dp的模板题,统计一个区间内不含62的数字个数和不含4的数字个数,直接拿数位dp的板子敲就行 ...

随机推荐

  1. sql server存储过程回滚事务

    SET NOCOUNT ON这个很常用 作用:阻止在结果集中返回显示受T-SQL语句或则usp影响的行计数信息. 当SET ONCOUNT ON时候,不返回计数,当SET NOCOUNT OFF时候, ...

  2. 手把手教你搭建FastDFS集群(中)

    手把手教你搭建FastDFS集群(中) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/u0 ...

  3. linux系统编程相关

    基本的概念:程序,进程,并发,单道程序设计,多道程序设计,时钟中断. 存储介质:寄存器(操作系统的位数是针对寄存器而言的,32位识字节,64位就是8字节).缓存cache.内存,硬盘,网络. cpu的 ...

  4. Vue学习笔记(一) 利用idea 搭建 vue 项目

    环境准备工作: 安装node.js 环境  -- 略 安装vue-li  全局安装vue-cli,在命令行中执行npm install -g vue-cli idea准备工作: 安装vue.js Fi ...

  5. 转:git上传本地项目到github

    转自:https://blog.csdn.net/Lucky_LXG/article/details/77849212 将本地项目上传到Github(两种简单.方便的方法) 一.第一种方法:首先你需要 ...

  6. 9 java 笔记

    1 所有的java对象都可以和字符串进行连接运算 2 在java中判断两个变量是否相等时:==运算符适用于基本类型的判断 3 JVM常量池保证相同的字符串直接量只有一个,不会产生多个相同的副本 4 O ...

  7. webpack整合 .vue 文件,集成 vue-loader

    webpack集成vue-loader 创建一个文件夹 test_webpack_vue 在 test_webpack_vue 下新建目录 src 在 src 目录下 新建文件 index.html ...

  8. ubuntu16.04环境LNMP实现PHP5.6和PHP7.2

    最近因为公司突然间说要升级php7,所以做个记录 PPA 方式安装 php7.2 : sudo apt-get install software-properties-common 添加 php7 的 ...

  9. DNS缓存失败怎么解决?

    DNS的中文名是域名系统,是域名和IP地址相互映射的一个分布式数据库.有了DNS,我们上网时直接输入网站域名(即网址)即可,而不用输入网站的IP地址访问网站,对于用户来说比较方便记忆和访问. 每次当我 ...

  10. 树莓派3B+一个外接显示器影响有线网卡无法启动的BUG

    在给一块用了不到一年的树莓派3B+重装官方系统时发现了一件诡异的事情,树莓派的有线网络会在重启后自动停掉,只有无线模块正常.即右上角的网络图标显示一个红叉叉. 在多次重装未果后,发现一个命令可以让有线 ...