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 }
View Code

 

 

posted @ 2016-03-04 20:19  妮king狼  阅读(524)  评论(0编辑  收藏  举报