题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089
回来再写,赶去大物实验。。
-------------------------------------------------------------------------------
刚开始学数位dp,简单说一下自己的理解。
预处理部分先略过。。。
下面是转移阶段:
以n=84举例,看数位dp如何工作的。(针对本题,即求出不含4且不含62的数字个数)
对第一位8:
如果取0--7,则肯定满足小于n,有bit[4]=8种选择。
再考虑后面一位,dp[1][2]表示含4或62的一位数的个数(只有一个4),ans+d[1][2]*bit[4]; {74,64,54,44,34,24,14,04}共8种
因为8>4,所以第一位可能取到4,当取4时,后面一位不论取什么都可以(为了不与上面的情况重复,限制后面的不能含有4或62)
dp[1][0]表示不含4或者62的一位数,即ans+=dp[i-1][0]; {40,41,42,43,45,46,47,48,49}共9种
因为8>6,所以第一位取6时,第二位可以取2. 再加上 ans+=dp[i-1][1]; {62},共1种
对第二位4, 所有情况均不满足,共0种。
所以一共有8+9+1=18个含4或62的数字,从分析可以看出求的是<84的范围,不含84。
好吧,我承认写的渣渣,,不过也理解了那么一丢丢
总的来说,就是从高位往低位进行,每完成一位就把该位固定,继续下一位。
分析每一位时都是从n向下取整(比如n=8000+,分析8时就是在分析0到7999),从而可以保证所有情况都小于n。
分析完后,固定该位(如上固定为8,继续分析下一位时,其实就是在分析8000到n),刚好不重复不遗漏。
再简单分析一个 8653 (下面加号意义自己理解)
第一步:(0--7)+(000--999)
第二步: 8+(0--5)+(00--99)
第三步: 8+6+(0--4)+(0--9)
第四步:8+6+5+(0-2)
感觉这样写稍微好些。。。
1 #include<cstdio> 2 #include<cstring> 3 #define ll long long 4 int dp[11][3]; 5 int bit[11]; 6 int n,m; 7 /* 8 dp[i][0] 前i位没有不吉利数字 9 dp[i][1] 前i位没有不吉利数字,但是第i位为2 10 dp[i][2] 前i位有不吉利数字 11 */ 12 void init() 13 { 14 memset(dp,0,sizeof(dp)); 15 dp[0][0]=1; 16 for(int i=1;i<11;i++) 17 { 18 dp[i][0]=dp[i-1][0]*9-dp[i-1][1]; 19 dp[i][1]=dp[i-1][0]; 20 dp[i][2]=dp[i-1][2]*10+dp[i-1][1]+dp[i-1][0]; 21 } 22 } 23 int evil(int x) 24 { 25 int tmp=x; 26 int ans=0; 27 int len=0; 28 while(x) 29 { 30 bit[++len]=x%10; 31 x/=10; 32 } 33 bit[len+1]=0; 34 int flag=0; 35 for(int i=len;i;i--) 36 { 37 ans+=dp[i-1][2]*bit[i]; 38 if(flag) ans+=dp[i-1][0]*bit[i]; 39 else 40 { 41 if(bit[i]>4) ans+=dp[i-1][0]; 42 if(bit[i+1]==6&&bit[i]>2) ans+=dp[i][1]; 43 if(bit[i]>6) ans+=dp[i-1][1]; 44 } 45 if(bit[i+1]==6&&bit[i]==2||bit[i]==4) flag=1; 46 } 47 return tmp-ans; 48 } 49 int main() 50 { 51 init(); 52 while(scanf("%d%d",&n,&m)&&(n||m)) 53 { 54 printf("%lld ",(ll)evil(m+1)-evil(n)); 55 } 56 }