题目的意思就是给出一个范围.问这个范围内有几个数 满足奇数位上的数,都比左右两边大.
我们要一位位判断,直到整个数结束.
我们拿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));
}
}