数位dp,今天学长讲的稍玄学,课下花了一会时间仔细看了一下,发现板子是挺好理解的,就在这里写一些:
数位dp主要就是搞一些在区间中,区间内的数满足题目中的条件的数的个数的一类题,题目一般都好理解,这时候就要使用今天介绍的数位dp;
比如这道例题:
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数字各出现了多少次。
求出在给定区间 [A,B] 内,符合条件 f(i) 的数 i 的个数。条件 f(i) 一般与数的大小无关,而与数的组成有关
由于数是按位dp,数的大小对复杂度的影响很小,这就是数位dp干的活!
题目看起来很简单,然后让我们做,我们会怎么做呢?反正我在没有学数位dp的时候当然是暴力枚举了,不用看1e12的数据范围一定会T到飞起,那我们怎么办呢?那就先让我来介绍一下数位dp吧!
数位dp的本质其实就是记忆化搜索,所以我们只要按照搜索的思路来做就好了!
我们来想一想如何设计这个搜索?
这个搜索,其实我感觉就像我们在考场上打的dfs暴力,记忆化搜索的过程就是
从起点向下搜索,到最底层得到方案数,一层一层向上返回答案并累加,最后从搜索起点得到最终答案。
对于 [l,r] 区间问题,我们一般把他转化为两次数位dp,即找 [0,r] 和 [0,l-1] 两段,再将结果相减就得到了我们需要的 [l,r];
所以这里的第一个套路就是遇见区间就要想前缀和!
如果理解了上述过程,我们需要考虑的就是怎样判断现在在哪一层,怎样判断当前的状态——这就需要我们传进一些参量。
我们一般设的dp状态就是f[i][.....]表示第i位......然后后面的就是题目要求的,所以就像理科生答文科题一样(虽然这个比喻并不恰当)我们就可以愉快的套板子了!
记忆化搜索的传参,我们一般传这几个:
1.搜到的位数pos,就是现在搜到了第几位;
2.lead值表示前面是不是全是前导0;1表示是,0表示不是!
3.最高位限制limit,同样0/1,表示是不是是这一位的最大的数!
4.根据题目中要求传的參!(这才是数位dp考察的!)
然后我们就来具体解释一下穿的參的一些细节!
关于limit:
如果当前位的limit==1并且去到了这一位的最大值,那么下一位limit=1;
如果当前位的limit==1但是没有取到最高值,那么下一位limit就等于0;
如果当前位的limit==0则下一位的limit==0
综上所述:这一位的数取i时,且这一位最高可以达到的值是res 则下一位的lmimit=(res==1&&limit)
关于lead标记:
如果前导0lead的值为1并且当前位的值是0,则pos+1继续搜索;
如果lead==1&&当前位不是0,那么本位可以做当前数的最高位pos+1接着搜索!
大概的dfs结构:
1.如果搜完了,就return 1;
2.如果没有最高位的限制并且已经搜过了 return value
3.获取当前位的最大数字,循环0到最大数字,循环内部根据题目的意思来判断;
如果前一位有前导0,下一位就随意,否则就按部就班的搜索就行了!
4.记得把这一位的dp值赋上!然后return!
A. Windy 数
题目描述
还记得引入的那道题吗?
「ZJOI2010」数字计数。
题目描述
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
输入格式
输入文件中仅包含一行两个整数a、b,含义如上所述。
输出格式
输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。
这道题就是上面的典型题目!传得参数也挺少的,就是只要方法是对的,无论什么样的复杂度都可以AC,这里在最后就介绍一个乱搞方法!
乱搞也是要技术的!
我们发现这道题的题目要求中并没有多少坑点,那么我们可以通过打表发现没增长一个数,每一位出现的次数都会增长相同的量,所以我们大胆的进行乱搞,由于这道题只要大点模一下大数,小点就直接暴力统计就完了!
乱搞代码!
////////////未完待更!!!/////////////////////