SPOJ BALNUM Balanced Numbers (数位dp)
题目:http://www.spoj.com/problems/BALNUM/en/
题意:找出区间[A, B]内所有奇数字出现次数为偶数,偶数字出现次数为计数的数的个数。
分析:
明显的数位dp题,首先,只有3种状态(0:没出现过, 1:数字出现奇数次, 2:数字出现偶数次),所以, 0~9 出现的次数就可以用3进制表示,最大的数就是 310 ,那么我们就可以把1019 哈希到310 内了。其中,我们可以假设:
(0:30 ,1:31 , 2:32 , .... , 9: 39 )
当第一次出现是,就把次数 +1, 否则,奇数变偶数(1-->2),偶数变奇数(2-->1)。因此,每个数字的变化在0~2内,3进制不会有冲突产生。
设记忆化数组f[20][310], 就是f[i][s] 表示取了前 i 位数字哈希后值为 s 的方法数。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 typedef unsigned long long ull; 7 const int N = 20; 8 9 ull f[N][60000]; 10 int dg[N]; 11 12 int check(int s) { 13 int nu[10]; 14 for(int i = 0; i < 10; ++i) { 15 int k = s % 3; 16 s /= 3; 17 if(!k) continue; 18 if((i&1) && (k==1)) return 0; 19 if(!(i&1) && (k==2)) return 0; 20 } 21 return 1; 22 } 23 24 int new_s(int d, int s) { 25 int nu[10]; 26 for(int i = 0; i < 10; ++i, s /= 3) nu[i] = s % 3; 27 28 if(nu[d] == 0) nu[d] = 1; 29 else nu[d] = 3 - nu[d]; 30 for(int i = 9; i > -1; --i) s = s * 3 + nu[i]; 31 return s; 32 } 33 34 ull dfs(int i, int s, bool flag, bool e) { 35 if(i == -1) return check(s); 36 if(!e && ~f[i][s]) return f[i][s]; 37 int res = 0; 38 int u = e ? dg[i] : 9; 39 for(int d = 0; d <= u; ++d) { 40 res += dfs(i-1, (flag==0&&d==0) ? 0 : new_s(d, s), flag||d>0, e&&d==u); 41 } 42 return e ? res : f[i][s] = res; 43 } 44 45 46 ull solve(ull x) { 47 int len = 0; 48 for( ; x; x /= 10) dg[len++] = x % 10; 49 return dfs(len-1, 0, 0, 1); 50 } 51 52 int main() 53 { 54 int T; 55 scanf("%d", &T); 56 ull a, b; 57 memset(f, -1, sizeof f); 58 while(T--) { 59 scanf("%llu %llu", &a, &b); 60 printf("%llu\n", solve(b)-solve(a-1)); 61 } 62 return 0; 63 }