FZU2109(数位DP)

来源:互联网 发布:slam算法工程师 薪资 编辑:程序博客网 时间:2024/05/19 18:39

题目的意思就是给出一个范围.问这个范围内有几个数  满足奇数位上的数,都比左右两边大.

我们要一位位判断,直到整个数结束.

我们拿0-34567这个范围来举例.

这里有两个不好理解的标志位.

首先我们要考虑的是前导0问题.(也就是最开始会出现很多不必要的0 ,因为564是符合条件的,但是我们表示时是00564 ,直到5这一位才算是开始了,所以5才是第0位)

首先我们要有个标志位,标记前面是不是全是前导0, 如果前面全是,并且我们要算的这一位还是0,那么接下去就还是按全是前导0来算.

只要出现一位不是0了,那就永远不是前导0,就算填的是0,它也不是前导0;

还有一个标志就是 前面几位是不是达到最大值..比如你第一位是3 ,第二位是4 ,那么第三位只能选择填充0到5 ,如果再大,就超过最大值了.但是如果你前面没有达到最大.前两位填了33那么接下去所有位数,都可以填到9,也不会超过最大值.       那么判断接下去是不是还有达到最大,要满足两个,第一前面达到最大,并且当前填充的这个数字仍然是最大.


解决了这两个标志位,就可以开始dp了.

如果这一位是奇数位,且填的数大于前面,就可以继续.或者这一位是偶数位,且填的数小于前面.

知道所有位数填充完了,返回成功了一个,最后计算成功了几个.

但如果直接不断递归,会超时.所以我们利用dp的记忆化搜索.

d[pos][num][odd]   pos代表当前是第几位了,num代表前一个数字是几,odd代表是奇数还是偶数..

首先你只到当前是第几位,也就是知道还剩下多少位要填,知道num,就知道当前要填数要和那个数字比较,当然还有奇偶行.

那是不是当这三个条件固定了,那么在这种情况下有几种可能是不是也固定了?

不一定,因为如果前面的值没打到最大,那么当前这位可以填0-9,那么没问题.

但是如果前面已经达到最大值了..那么当前这位就不能填0-9了,而是填0到这个数的最大值.

那么就不是唯一确定的值,就不能记忆化搜索(结果可能和你记录的不一样.)

为了避免.我们就只在前面没有达到最大值时,才采用记忆化搜索.



AC代码:


#include<stdio.h>#include<string.h>int d[12][12][2];int diet[12];int dfs(int  pos , int pre0 , int isodd , int num , int s) {if(pos == -1)return 1;if(!s && d[pos][num][isodd] != -1)return d[pos][num][isodd];int upper = s ? diet[pos] : 9;int ans = 0;for (int i = 0 ; i <= upper ; i++) {if (i == 0 && pre0) {ans += dfs(pos - 1 , 1 ,isodd , 9 , 0);}else if(isodd && i <= num)ans += dfs(pos - 1 , 0 , isodd ^ 1 , i , s && (i == upper));else if(!isodd && i >= num)ans += dfs(pos - 1 , 0 , isodd ^ 1 , i , s && (i == upper));}if(!s)d[pos][num][isodd] = ans;return ans;}int cul(int x) {int pos = 0;while(x) {diet[pos++] = x % 10;x /= 10;}return dfs(pos - 1 , 1 , 1 , 9 , 1);}int main () {int t ; int l,r;scanf("%d",&t);memset(d , -1 ,sizeof(d));while(t--) {scanf("%d%d",&l,&r);printf("%d\n",cul(r) - cul(l - 1));}}


0 0
原创粉丝点击