数位dp:

数位dp是一种计数用的dp,一般就是要统计一个区间[li,ri]内满足一些条件数的个数。所谓数位dp,
字面意思就是在数位上进行dp。数位的含义:一个数有个位、十位、百位、千位......数的每一位就是数位.一般都通过
dfs来递归找寻。

数位dp与dfs爆搜之间的区别就是前者会记录状态以便优化下一次寻找的时间(也就是记忆化操作)

数位dp操作:

例如给你一个区间[12,12345],你需要找到这个区间内满足条件的数的个数。这个时候我们一般都是先找[0,12345]这个区间满足条件数的个数,然后减去[0,11]这个区间满足条件的数的个数

这里用[0,12345]来演示:

我们先把12345这一个int类型的数分开放在int v[]数组里面(意思就是1放在数组v[0]位,2放在v[1]位........),然后我们从最高位开始枚举,刚开始我们枚举最高位可以是[0,1],如果你选择的是0那么下一位的枚举范围就是[0,9],否则枚举范围就是[0,2]。一旦第二位的枚举范围是[0,9]那么之后枚举其他位置他的枚举范围都是[0,9](想想就知道啦!)。也就是说如果枚举第三位的时候想让它的枚举范围变成[0,3]那么第一位必须枚举1第二位必须枚举2

然后就是最重要的记忆化,一般记忆化都需要找出来记录状态的方程(也就是dp方程)。

上一个模板看看:模板原博客

 1 typedef long long ll;
2
3 int a[20];
4
5 ll dp[20][state];//不同题目状态不同
6
7 ll dfs(int pos,/*state变量*/,bool lead/*前导零*/,bool limit/*数位上界变量*/)//不是每个题都要判断前导零
8
9 {
10
11 //递归边界,既然是按位枚举,最低位是0,那么pos==-1说明这个数我枚举完了
12
13 if(pos==-1) return 1;/*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */
14
15 //第二个就是记忆化(在此前可能不同题目还能有一些剪枝)
16
17 if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state];
18
19 /*常规写法都是在没有限制的条件记忆化,这里与下面记录状态是对应,具体为什么是有条件的记忆化后面会讲*/
20
21 int up=limit?a[pos]:9;//根据limit判断枚举的上界up;这个的例子前面用213讲过了
22
23 ll ans=0;
24
25 //开始计数
26
27 for(int i=0;i<=up;i++)//枚举,然后把不同情况的个数加到ans就可以了
28
29 {
30
31 if() ...
32
33 else if()...
34
35 ans+=dfs(pos-1,/*状态转移*/,lead && i==0,limit && i==a[pos]) //最后两个变量传参都是这样写的
36
37 /*这里还算比较灵活,不过做几个题就觉得这里也是套路了
38
39 大概就是说,我当前数位枚举的数是i,然后根据题目的约束条件分类讨论
40
41 去计算不同情况下的个数,还有要根据state变量来保证i的合法性,比如题目
42
43 要求数位上不能有62连续出现,那么就是state就是要保存前一位pre,然后分类,
44
45 前一位如果是6那么这意味就不能是2,这里一定要保存枚举的这个数是合法*/
46
47 }
48
49 //计算完,记录状态
50
51 if(!limit && !lead) dp[pos][state]=ans;
52
53 /*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/
54
55 return ans;
56
57 }
58
59 ll solve(ll x)
60
61 {
62
63 int pos=0;
64
65 while(x)//把数位都分解出来
66
67 {
68
69 a[pos++]=x%10;//个人老是喜欢编号为[0,pos),看不惯的就按自己习惯来,反正注意数位边界就行
70
71 x/=10;
72
73 }
74
75 return dfs(pos-1/*从最高位开始枚举*/,/*一系列状态 */,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛
76
77 }
78
79 int main()
80
81 {
82
83 ll le,ri;
84
85 while(~scanf("%lld%lld",&le,&ri))
86
87 {
88
89 //初始化dp数组为-1,这里还有更加优美的优化,后面讲
90
91 printf("%lld\n",solve(ri)-solve(le-1));
92
93 }
94
95 }

例题讲解:

题目:

杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。

不吉利的数字为所有含有4或62的号码。例如:

62315 73418 88914

都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。

你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。

Input输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。

Output对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。

Sample Input

1 100
0 0

Sample Output

80

题解:

首先不要4,那么dfs过程中遇到4就直接continue掉就可以了。

然后就是不要62,这样的话我们就要在dfs过程中弄一个参数来记录一下它的上一位是谁。如果上一位是6那么就对后面的枚举范围有影响。否则就没有影响!

因为上一位是6的话会对之后的选择造成影响,所以如果上一位是6要开一个新状态来记录(这就是dp数组第二个维度的由来,那么第一个维度表明现在枚举到哪个位置了)。

代码:

 1 #include<stdio.h>
2 #include<string.h>
3 #include<algorithm>
4 #include<iostream>
5 using namespace std;
6 const int maxn=105;
7 int a[maxn],dp[maxn][2];
8 //pos是用来记录遍历到那个位置了,pre用来记录上一个数是谁,sta用来记录dp状态,limit室枚举上界
9 int dfs(int pos,int pre,bool sta,bool limit)
10 {
11 if(pos==-1) return 1; //pos==-1的时候就结束了,返回1代表这个号码可以构成号码牌
12 if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
13 int up=limit?a[pos]:9;
14 int tmp=0;
15 for(int i=0;i<=up;++i)
16 {
17 if(pre==6 && i==2) continue;
18 if(i==4) continue;
19 tmp+=dfs(pos-1,i,i==6,limit && i==a[pos]);
20 }
21 if(!limit) dp[pos][sta]=tmp; //只有上界不等于a[pos]的时候,才会记录,正好和上面记忆化return保持一致
22 //为什么要加一个判断,因为如果它的上界等于a[pos]的时候会对后面的枚举造成影响
23 //而且这样记忆化可以优化更多的时间
24 return tmp;
25 }
26 int solve(int ans)
27 {
28 int pos=0;
29 while(ans)
30 {
31 a[pos]=ans%10;
32 ans/=10;
33 pos++;
34 }
35 return dfs(pos-1,0,0,true);
36 }
37 int main()
38 {
39 int l,r;
40 while(~scanf("%d%d",&l,&r) && l+r)
41 {
42 memset(dp,-1,sizeof(dp));
43 printf("%d\n",solve(r)-solve(l-1));
44 }
45 return 0;
46 }

题目:

反恐怖分子在尘土中发现了一颗定时炸弹。但这一次,恐怖分子改进了定时炸弹。定时炸弹的序号从1数到n,如果当前的序号包含子序号“49”,则爆炸的威力会增加1分。现在反恐怖分子知道了数字n,他们想知道最后的权力点。你能帮助他们吗?

输入第一行输入由一个整数T (1 <= T <= 10000)组成,表示测试用例的数量。对于每个测试用例,都有一个整数N (1 <= N <= 2^63-1)作为描述。输入在文件标记结束时终止。输出对于每个测试用例,输出一个整数,指示电源的最终点。

Sample Input

3
1
50
500

Sample Output

0
1
15

提示从1到500年,数字,包括子“49”是“49”、“149”、“249”、“349”、“449”、“490”、“491”、“492”、“493”、“494”、“495”、“496”、“497”、“498”、“499”,所以答案是15。

题解:

比上一个还简单,不用讲了

代码:

 1 #include<stdio.h>
2 #include<string.h>
3 #include<algorithm>
4 #include<iostream>
5 using namespace std;
6 const int maxn=105;
7 typedef long long ll;
8 ll v[maxn],dp[maxn][2];
9 ll dfs(ll pos,ll pre,ll sta,bool limit)
10 {
11 if(pos==-1) return 1;
12 if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
13 ll up=limit?v[pos]:9;
14 ll tmp=0;
15 for(ll i=0;i<=up;++i)
16 {
17 if(pre==4 && i==9) continue;
18 tmp+=dfs(pos-1,i,i==4,limit && i==v[pos]);
19 }
20 if(!limit) dp[pos][sta]=tmp;
21 //只有上界为9的时候才会往dp数组里面存,因为这样能节省更多的时间
22 return tmp;
23 }
24 ll solve(ll ans)
25 {
26 ll pos=0;
27 while(ans)
28 {
29 v[pos++]=ans%10;
30 ans/=10;
31 }
32 return dfs(pos-1,-1,0,true);
33 }
34 int main()
35 {
36 ll t,n;
37 scanf("%I64d",&t);
38 while(t--)
39 {
40
41 memset(dp,-1,sizeof(dp));
42 scanf("%I64d",&n);
43 printf("%I64d\n",n-solve(n)+1);
44 }
45 return 0;
46 }

数位dp整理 && 例题HDU - 2089 不要62 && 例题 HDU - 3555 Bomb的更多相关文章

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

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

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

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

  3. HDU 2089 不要62(数位dp模板题)

    http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:求区间内不包含4和连续62的数的个数. 思路: 简单的数位dp模板题.给大家推荐一个好的讲解博客.h ...

  4. [hdu 2089] 不要62 数位dp|dfs 入门

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:求[n, m]区间内不含4和62的数字个数. 这题有两种思路,直接数位dp和dfs 数位d ...

  5. HDU 2089 不要62 数位DP模板题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 参考博客:https://www.cnblogs.com/HDUjackyan/p/914215 ...

  6. hdu 2089 不要62【数位dp】

    HDU 2089 求给定区间内不含62和4的数的个数. 数位dp入门.从这里我清楚了一些数位dp的用法.比如limit是判断是否达到上界,而且需要判断(!limit)..比如若题目要求不含11的个数, ...

  7. HDU 2089 不要62(数位dp入门)

    题意:统计区间 [a,b] 中不含 4 和 62 的数字有多少个. 题解:这是数位DP的入门题了,首先要理解数DP的原理,DP[i][j]:代表第i位的第j值,举个栗子:如4715   数位数是从右向 ...

  8. 【数位DP】Hdu 2089:不要62

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

  9. hdu 2089 不要62(入门数位dp)

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

随机推荐

  1. Java 用java GUI写一个贪吃蛇小游戏

    目录 主要用到 swing 包下的一些类 上代码 游戏启动类 游戏数据类 游戏面板类 代码地址 主要用到 swing 包下的一些类 JFrame 窗口类 JPanel 面板类 KeyListener ...

  2. VMware下安装Ubantu 18.04

    一.VIM安装及配置 1.安装VIM sudo apt-get install vim 二.拼音输入法以及搜狗拼音输入法安装 1.安装Fcitx输入框架 sudo apt-get install fc ...

  3. Sentry(v20.12.1) K8S 云原生架构探索,JavaScript Enriching Events(丰富事件信息)

    系列 Sentry-Go SDK 中文实践指南 一起来刷 Sentry For Go 官方文档之 Enriching Events Snuba:Sentry 新的搜索基础设施(基于 ClickHous ...

  4. 备份和还原Windows DHCP服务器

    在本教程中,您将学习如何使用DHCP控制台和PowerShell备份和还原Windows DHCP服务器. 您是否曾经经历过DHCP服务器崩溃或故障?在设备开始重新启动之前,一切都会平静. 用户将抱怨 ...

  5. 前端面试准备笔记之JavaScript(03)

    01. 变量声明提升 在预解析的时候,成员变量和函数,被提升到最高的位置,方便其他程序访问. 可以先使用后声明. 只提升变量名,不提升变量值 let const 声明的变量不具有变量声明提升. // ...

  6. 树莓派做私有云盘-极简版(owncloud)

    这里直接给出配置好私有云的镜像,只需烧录镜像后微改配置后即可使用 链接:https://pan.baidu.com/s/1EOQaSQso-0wmnuWgZKknZg提取码:q26h 1.直接将此镜像 ...

  7. (SqlServe)关于字符串长度被截断的问题

    1. 问题描述 在同步数据时常常会发现一个错误:将截断字符串或二进制数据. 2. 问题原因 这个问题出现的原因是:要插入的数值字段的长度超出了数据库中字段的长度.比如:插入的字符串字节长度是40,数据 ...

  8. Zookeeper C API的学习 以及样例 很赞

    https://www.cnblogs.com/haippy/archive/2013/02/21/2920280.html

  9. URL重定向漏洞解析

    参考文章 悟空云课堂 | 第二期:URL重定向(跳转)漏洞 CWE-601: URL Redirection to Untrusted Site ('Open Redirect') 分享几个绕过URL ...

  10. Prometheus-process exporter-进程监控

    Prometheus-process exporter-进程监控 1.下载安装 2.修改配置文件 3.启动 相关内容原文地址: 博客园:落烨无痕:process exporter 配置项解释 huan ...