这道题应该是最简单的数位dp了,看了09年的刘聪的国家集训队论文,入门了一下数位dp。解决这道题还是花了我好长的一段时间。
这里就先讲一下我是如何用数位dp解这道题的,感觉后面还是要写一篇数位dp的总结。首先是状态的确定,数位dp的基本状态往往是dp[i, ...]i表示枚举到的位数,后面是需要的状态。对于这道题,我列举了一个状态就是dp[i][j]表示枚举到第i位第一个数字是j所包含的合法数字数。
程序结构:
初始化的时候把dp数组先预处理出来,后面写个solve(x)表示当前数前面的合法数个数。那ans = solve(b+1)-solve(a)。
关键在于处理solve(x)的计算。首先是枚举每一位,然后是枚举每一位可能出现的值,注意在枚举值得时候一旦前面已经枚举的数中已经出现4或者是62直接break。
若是前一位确定为6这一位枚举到2 或者 这一位为4 continue,这应该都比较好理解。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 #include <algorithm> 7 #define LEN 30 8 using namespace std; 9 10 int a, b, dp[LEN][LEN]; 11 12 void init() 13 { 14 memset(dp, 0, sizeof dp); 15 for(int i=0; i<LEN; i++){ 16 if(i!=4) dp[1][i] = 1; 17 } 18 for(int i=2; i<LEN; i++){ 19 for(int j=0; j<10; j++){ 20 if(j==4) continue; 21 for(int k=0; k<10; k++){ 22 if((j==6 && k==2) || k==4)continue; 23 dp[i][j] += dp[i-1][k]; 24 } 25 } 26 } 27 } 28 29 30 int solve(int x) 31 { 32 char str[100] = {0}; 33 sprintf(str+1, "%d", x); 34 int len = strlen(str+1), ret = 0; 35 for(int i=len; i>=1; i--){ 36 int pos = len-i+1, num = str[pos]-'0'; 37 for(int j=0; j<num; j++){ 38 if(j==4 || (j==2 && str[pos-1]=='6'))continue; 39 ret += dp[i][j]; 40 } 41 if(num==4 || (num==2 && str[pos-1]=='6')) break; 42 } 43 return ret; 44 } 45 46 int main() 47 { 48 // freopen("in.txt", "r", stdin); 49 50 while(scanf("%d%d", &a, &b)!=EOF) 51 { 52 if(a==0 && b==0)break; 53 init(); 54 int ans = solve(b+1) - solve(a); 55 printf("%d ", ans); 56 } 57 return 0; 58 }