数位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. (十)Python装饰器

    装饰器:本质就是函数,功能是为其他函数添加附加功能. 两个原则: 1.不修改被修饰函数的源代码 2.不修改被修饰函数的调用方式 一个栗子 def test(): res = 0 for i in ra ...

  2. 【System】I/O密集型和CPU密集型工作负载之间有什么区别

    CPU密集型(CPU-bound) CPU密集型也叫计算密集型,指的是系统的硬盘.内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/ ...

  3. kubernets之机理概览

    一  了解kubernets的运行机理 1.1  了解架构 众所周知,kubernets的组成由2个部分组成 kubernets  平面 node节点  (工作节点) 控制平面的组成 etcd  分布 ...

  4. CTS相关的几个表

    TMSALOG/TMSALOGAR :STMS传输日志表 TMSCNFS:传输组 TMSCDOM:传输域 TMSCDES:传输目的地 TMSBUFTXT:传输请求的短文本和用户 TMSCSYS:tms ...

  5. 前端知识(一)05 axios-谷粒学院

    目录 一.axios的作用 二.axios实例 1.复制js资源 2.创建 axios.html 3.引入js 4.启动课程中心微服务 5.编写js 6.html渲染数据 7.跨域 8.使用生命周期函 ...

  6. .NET 中依赖注入组件 Autofac 的性能漫聊

    Autofac 是一款超赞的 .NET IoC 容器 ,在众多性能测评中,它也是表现最优秀的一个.它管理类之间的依赖关系, 从而使 应用在规模及复杂性增长的情况下依然可以轻易地修改.它的实现方式是将常 ...

  7. 为什么Go自带的日志默认输出到os.Stderr?

    为什么Go自带的日志默认输出到os.Stderr? - 知乎 https://www.zhihu.com/question/67629357 Note that the Go runtime writ ...

  8. Git:.gitignore和.gitkeep文件的使用 让空文件夹被跟踪

    Git:.gitignore和.gitkeep文件的使用 Git:.gitignore和.gitkeep文件的使用 https://majing.io/posts/10000001781172 .gi ...

  9. 隐性 URL 转发代码

    隐性转发的优势体现于无需跳转和变动浏览器地址栏,即可实现转发. <!DOCTYPE html> <html lang="zh-CN"> <head&g ...

  10. 语言反射规则 - The Laws of Reflection

    [译]Go反射的三个原则(官方博客) | seven的分享 https://sevenyu.top/2019/12/21/laws-of-reflection.html wilhg/The-Laws- ...