数位dp

acwing 1081.度的数量

https://www.acwing.com/problem/content/1083/

 

题意:找出区间$(X, Y) $中,用$B$进制表示并且只有$K$个1的数有多少。

类似前缀和的思想,可以先求$0$到$Y$中满足条件的数的个数,减去 $0$到$X$中满足条件的数的个数。

将数字标号,从最高位开始。对每一位进行分类讨论, 例如对于第一位数字$a_{n-1}$, 左子树代表从$0$到$a_{n-1}-1$中选取一个数字填在第一位,右子树代表该位填的数字就是$a_{n-1}$。

由于本题的数字只能填$0$或$1$, 所以左子树的情况就是$0$,右子树的情况就是填$1$。分析可知,左子树的情况实际就是组合数,所以我们要预处理组合数。

例如对于第一位的左子树,因为第一位填$0$,所以在后面的$n-1$位数中需要填入$K$个1,即$C_{n-1}^{K}$。

 1 #include <iostream>
 2 #include <algorithm>
 3 
 4 using namespace std;
 5 
 6 const int N = 35;
 7 int K, B;
 8 int c[N][N];
 9 
10 int dp(int n)
11 {
12     if(!n)return 0;
13     
14     vector<int> nums;
15     while(n)nums.push_back(n % B), n /= B;
16     
17     int last = 0;//记录当前所需要的前置信息,这里是前面1的个数
18     int res = 0;
19     for(int i = nums.size() - 1 ; i >= 0 ; i --)
20     {
21         int x = nums[i];
22         if(x)//左子树
23         {
24             res += c[i][K - last];//当前位填0
25             if(x > 1)//如果当前位能填1,那么右子树就不存在了
26             {
27                 if(K - last - 1 >= 0)res += c[i][K - last - 1];
28                 break;
29             }
30             else//当前位填1
31             {
32                 last ++;
33                 if(last > K)break;
34             }
35         }
36         if(!i && last == K)res ++;
37     }
38     
39     return res;
40 }
41 
42 void init()
43 {
44     for(int i = 0 ; i < N ; i ++)
45         for(int j = 0 ; j <= i ; j ++)
46             if(!j)c[i][j] = 1;
47             else c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
48 }
49 
50 int main(){
51     int l, r;
52     init();
53     cin >> l >> r >> K >> B;
54     
55     cout << dp(r) - dp(l - 1) << endl;
56     return 0;
57 }

 

acwing 1082.数字游戏

https://www.acwing.com/problem/content/1084/

同上题分析,只是预处理的东西不同,该题需要预处理上升数的个数。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <vector>
 5 
 6 using namespace std;
 7 
 8 const int N = 11;
 9 
10 int f[N][10];
11 
12 void init()
13 {
14     for(int i = 0 ; i <= 9 ; i ++)f[1][i] = 1;
15     for(int i = 2 ; i < N ;  i ++)
16         for(int j = 0 ; j <= 9 ; j ++)
17             for(int k = j ; k <= 9 ; k ++)
18                 f[i][j] += f[i - 1][k];
19 }
20 
21 int dp(int n)
22 {
23     if(!n)return 1;
24     
25     vector<int> nums;
26     while(n)nums.push_back(n % 10), n /= 10;
27     
28     int res = 0;
29     int last = 0;
30     for(int i = nums.size() - 1; i >= 0 ; i --)
31     {
32         int x = nums[i];
33         for(int j = last ; j < x ; j ++)
34             res += f[i + 1][j];//数组存储数字是从0开始,我们算位数是从1开始,需要加上偏移量
35         
36         if(x < last)break;
37         
38         last = x;
39         
40         if(!i)res ++;
41     }
42     return res;
43 }
44 
45 int main(){
46     int l, r;
47     init();
48     while(cin >> l >> r)cout << dp(r) - dp(l - 1) << endl;
49     return 0;
50 }

 

 

 acwing 1083.Windy数

https://www.acwing.com/problem/content/1085/

同第一题分析,因为不能有前导零。所以有些许不同。

第一分支的第一个数字不能为$0$。左子树的和依旧是简单的$dp$。需要注意的是需要在最后加上所有有前导零的数字情况。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <vector>
 5 
 6 using namespace std;
 7 
 8 const int N = 35;
 9 int f[N][10];
10 
11 void init()
12 {
13     for(int i = 0 ; i <= 9 ; i ++)f[1][i] = 1;
14     for(int i = 2 ; i < N ; i ++)
15         for(int j = 0 ; j <= 9 ; j ++)
16             for(int k = 0 ; k <= 9 ; k ++)
17                 if(abs(j - k) >= 2)
18                     f[i][j] += f[i - 1][k];
19 }
20 
21 int dp(int n)
22 {
23     if(!n)return 0;
24     
25     vector<int> nums;
26     while(n)nums.push_back(n % 10), n /= 10;
27     
28     int res = 0;
29     int last = -2;
30     for(int i = nums.size() - 1 ; i >= 0 ; i --)
31     {
32         int x = nums[i];
33         for(int j = i == nums.size() - 1 ; j < x ; j ++)
34             if(abs(j - last) >= 2)
35                 res += f[i + 1][j];
36         
37         if(abs(x - last) < 2)break;
38         
39         last = x;
40         
41         if(!i)res ++;
42     }
43     
44     for(int i = 1 ; i < nums.size() ; i ++)//将位数是1, 2,3..., nums.size() - 1的windy数加上
45         for(int j = 1 ; j <= 9 ; j ++)
46             res += f[i][j];
47     
48     return res;
49 }
50 
51 
52 int main(){
53     int l, r;
54     init();
55     cin >> l >> r;
56     cout << dp(r) - dp(l - 1) << endl;
57     return 0;
58 }

 

 

acwing 1084.数字游戏②

https://www.acwing.com/problem/content/1086/

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <vector>
 5 
 6 using namespace std;
 7 
 8 const int N = 35;
 9 int P;
10 int f[N][10][110];
11 
12 int mod(int x, int y)//将取模都转为正数
13 {
14     return (x % y + y) % y;
15 }
16 
17 void init()
18 {
19     memset(f, 0, sizeof f);//多组输入,因为P不一样所以每次都要重做一次
20     for(int i = 0 ; i <= 9 ; i ++)f[1][i][i % P] = 1;
21     for(int i = 2 ; i < N ; i ++)
22         for(int j = 0 ; j <= 9 ; j ++)
23             for(int k = 0 ; k < P ; k ++)
24                 for(int x = 0 ; x <= 9 ; x ++)
25                     f[i][j][k] += f[i - 1][x][mod(k - j, P)];
26 }
27 
28 
29 int dp(int n)
30 {
31     if(!n)return 1;
32     
33     vector<int> nums;
34     while(n)nums.push_back(n % 10), n /= 10;
35     
36     int res = 0;
37     int last = 0;
38     for(int i = nums.size() - 1 ; i >= 0 ; i --)
39     {
40         int x = nums[i];
41         for(int j = 0 ; j < x ; j ++)
42             res += f[i + 1][j][mod(-last, P)];
43         
44         last += x;
45         
46         if(!i && last % P == 0)res ++;
47     }
48     return res;
49 }
50 
51 int main(){
52     int l, r;
53     while(cin >> l >> r >> P)
54     {
55         init();
56         cout << dp(r) - dp(l - 1) << endl;
57     }
58     return 0;
59 }

 

 

acwing 1085.不要62

https://www.acwing.com/problem/content/1087/

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <vector>
 5 
 6 using namespace std;
 7 
 8 const int N = 11;
 9 int f[N][10];
10 
11 void init()
12 {
13     for(int i = 0 ; i <= 9 ; i ++)
14         if(i != 4)f[1][i] = 1;
15     for(int i = 1 ; i < N ; i ++)
16         for(int j = 0 ; j <= 9 ; j ++)
17             for(int k = 0 ; k <= 9 ; k ++)
18             {
19                 if(j == 4 || k == 4 || j == 6 && k == 2)continue;
20                 f[i][j] += f[i - 1][k];
21             }
22 }
23 
24 
25 int dp(int n)
26 {
27     if(!n)return 1;
28     
29     vector<int> nums;
30     while(n)nums.push_back(n % 10), n /= 10;
31     
32     int res = 0;
33     int last = 0;
34     for(int i = nums.size() - 1 ; i >= 0 ; i --)
35     {
36         int x = nums[i];
37         for(int j = 0 ; j < x ; j ++)
38         {
39             if(j == 4 || last == 6 && j == 2)continue;
40             res += f[i + 1][j];
41         }
42         
43         if(x == 4 || last == 6 && x == 2)break;
44         
45         last = x;
46         
47         if(!i)res ++;
48     }
49     return res;
50 }
51 
52 int main(){
53     int l, r;
54     init();
55     while(cin >> l >> r, r || l)
56     {
57         cout << dp(r) - dp(l - 1) << endl;
58     }
59     return 0;
60 }

 

posted @ 2020-04-12 18:09  dzcixy  阅读(147)  评论(0)    收藏  举报