数位dp——统计'1'的个数

  今天去牛客网看了看 包含一 这道题,一开始没看清,以为它要统计 1~n 所有数中数字 ‘1‘ 出现的总次数,也就是说,若 n == 11,则 ans = 4;而按照题目的原意 ans 应该为 3。看错题意后还是挣扎了好久,具体的调试过程也不想回忆叙述了,先贴上按照我一开始理解的意思的代码吧,虽然没有题目让我测,但我和自己写的暴力法对拍过,应该没问题的。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<vector>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long long LL;
 7
 8 LL C[12][12], p9[12] = {1,};
 9 // C 数组为组合数,p9 是 9 的幂数组
10 inline void initC(const int &n = 11) {
11     for(int i = 0; i <= n; ++i)
12         C[i][0] = 1;
13     for(int i = 1; i <= n; ++i)
14         for(int j = 1; j <= n; ++j)
15             C[i][j] = C[i-1][j-1] + C[i-1][j];
16     for(int i = 1; i <= n; ++i)
17         p9[i] = p9[i-1] * 9;
18 }
19
20 LL all[16], num0[16], num1[16];
21 // all[i] 表示 i 位数的个数,num0[i] 表示不含数字 1 的 i 位数的个数
22 // num1[i] 表示 i 位数中含有数字 1 出现的总次数,注意是 ‘1‘ 出现的总次数!求法有点麻烦
23 inline void init(const int &k = 11) {
24     all[0] = 1;
25     num0[0] = 1;
26     for(int i = 1; i <= k; ++i) {
27         all[i] = all[i-1] * 10;
28         num0[i] = num0[i-1] * 9;
29     }
30     initC();
31     for(int n = 1; n <= k; ++n) {
32         LL &sum = num1[n];
33         sum = 0;
34         for(int i = 1; i <= n; ++i)
35             sum += i * C[n][i] * p9[n-i];
36     }
37 }
38
39 // 和数位 dp 的分析步骤有点类似
40 inline LL solve(const LL &n) {
41     LL digit[12], len = 0, x = n;
42     while(x) {
43         digit[++len] = x % 10;
44         x /= 10;
45     }
46     int count = 0;      // 统计前 i 位数字 1 的个数
47     LL sum = 0;
48     // 核心计数部分(结合曾经做过的数位 dp 的思路来考虑)
49     for(LL i = len; i > 0; --i) {
50         sum += digit[i] * num1[i-1];        // 先加上当第 i 位取 0~digit[i]-1 时,i-1 位数的数字 1 的总个数
51         if(count)    sum += count * digit[i] * all[i-1];    // 统计前面的 1 的个数(第 i 位后取任何数字都没所谓)
52         if(digit[i] == 1)   ++count;            // 计数器加 1
53         if(digit[i] > 1)   sum += all[i-1];         // 统计若当前位取 1 时的个数(同理后面是什么都没所谓)
54     }
55     // 好好体会下这个数位 dp 的思路,要做到不重不漏真不容易~
56     return sum;
57 }
58
59 // 分解统计 ‘1‘ 的个数
60 inline LL caclu(LL x) {
61     LL res = 0;
62     while(x) {
63         if(x % 10 == 1)  ++res;
64         x /= 10;
65     }
66     return res;
67 }
68
69 // 暴力枚举统计
70 inline LL test(const LL &x) {
71     LL sum = 0;
72     for(LL i = 1; i <= x; ++i)
73         sum += caclu(i);
74     return sum;
75 }
76
77 int main() {
78     LL n;
79     init();
80     while(~scanf("%I64d",&n))
81         printf("solve = %I64d  test = %I64d\n\n",solve(n+1),test(n));
82     return 0;
83 }

统计 1~n 中数字‘1‘出现的总次数

  后来,按照题目的意思我又重做了一遍:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5
 6 int dp[3][16];
 7 // dp[0][i] 表示包含 1 的 i 位数的个数
 8 // dp[1][i] 表示以 1 开头的不包含 1 的 i 位数的个数
 9 // dp[2][i] 表示不包含 1 的 i 位数的个数
10 // 其实 dp[1][i] 和 dp[2][i] 实质是一样的,合并成一个就行了,所以空间和时间都能提升一点;为了让它更直观,我就不改了
11 inline void init(int n = 11) {
12     dp[2][0] = 1;
13     for(int i = 1; i <= n; ++i) {
14         dp[0][i] = 10 * dp[0][i-1] + dp[2][i-1];
15         dp[1][i] = dp[2][i-1];
16         dp[2][i] = dp[2][i-1] * 9;
17     }
18 }
19
20 inline int solve(int x) {
21     int digit[12], len = 0;
22     while(x) {
23         digit[++len] = x % 10;
24         x /= 10;
25     }
26     bool flag = 0;
27     int sum = 0;
28     for(int i = len; i; --i) {
29         sum += digit[i] * dp[0][i-1];
30         if(flag)    sum += digit[i] * dp[2][i-1];
31         else if(digit[i] > 1)    sum += dp[1][i];
32         if(digit[i] == 1)   flag = 1;
33     }
34     return sum;
35 }
36
37 const int inf = 0x7fffffff;
38
39 int main() {
40     int n;
41     init();
42     while(~scanf("%d",&n)) {       // 有符号 int 的上限,要注意处理好
43         if(n == inf)    printf("%d\n",solve(n) + 1);
44         else    printf("%d\n",solve(n+1));
45     }
46     return 0;
47 }

统计包含‘1‘的数字的个数

  耗费了一个中午和下午时间写完这两个代码后,我发觉我对于数位 dp 已经感到无爱了,再给我来一道这样的题的话就真的要挂了~

---------------------------------------------来填一下坑先---------------------------------------------------

  后来发现,原来还有这样的一道题 统计一,把我第一个代码的 I64d 改为 lld 以及输出修改一下就能过,果然我的做法是对哦~

数位dp——统计'1'的个数

时间: 2024-11-07 18:45:47

数位dp——统计'1'的个数的相关文章

ACM学习历程—HDU5587 Array(数学 &amp;&amp; 二分 &amp;&amp; 记忆化 || 数位DP)(BestCoder Round #64 (div.2) 1003)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5587 题目大意就是初始有一个1,然后每次操作都是先在序列后面添加一个0,然后把原序列添加到0后面,然后从0到末尾,每一个都加上1. 例如:a0, a1, a2 => a0, a1, a2, 1, a0+1, a1+1, a2+1 题解中是这么说的:“ 其实Ai为i二进制中1的个数.每次变化A{k+2^i}=A{k}+1,(k<2^?i??)不产生进位,二进制1的个数加1.然后数位dp统计前m个数二

hdu3709(求区间内平衡数的个数)数位dp

题意:题中平衡数的定义: 以一个位置作为平衡轴,然后左右其他数字本身大小作为重量,到平衡轴的距离作为全职,实现左右平衡(即杠杆原理平衡).然后为区间[x,y]内平衡数的个数. (0 ≤ x ≤ y ≤ 1018) 解法:数位dp.如果一个数的平衡数,那么它的平衡轴位置是确定的.原来一直尝试数位dp在dfs时候列举平衡轴的位置,后来才意识到可以提前枚举平衡轴位置,然后再dfs,这样比较好写.dp[mid][pre][wei];表示对称轴是mid,计算第pre个位置以后需要力矩大小wei的数的个数.

【数位DP】Codeforces Gym 100418J Lucky tickets

题意: 设性质P:一个数能够整除它二进制表示下的1的个数.求[1,N]中满足性质P的数的个数.N<=1019. 思路: 数位DP.首先这个数最多有64位,我们可以枚举1的个数x,然后求可以整除x的数的个数.设dp[i][j][k][w]表示从最高位枚举到i位,现在已经构成的数模x余多少(这里是关键,只用考虑余数),现在已经用了k个1,w=0表示现在枚举的这个数已经小于N了,w=1表示从最高位到第i位都与N相同(数位dp计算时的方法:如果当前枚举的数小于N了,那么枚举的这一位就可以任意,如果等于N

(数位DP 1.2)hdu 3555 Bomb(统计1~n中,包含49的数的个数)

题目: Bomb Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 9273    Accepted Submission(s): 3275 Problem Description The counter-terrorists found a time bomb in the dust. But this time the terrori

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

[ACM] hdu 3555 Bomb (数位DP,统计1-N中含有“49”的总数)

Bomb Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 7187 Accepted Submission(s): 2512 Problem Description The counter-terrorists found a time bomb in the dust. But this time the terrorists impro

(数位DP 1.1)hdu 2089 不要62(求一个区间[a,b]中不包含4,64的数的个数)

题目: 不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 23759    Accepted Submission(s): 8128 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer).杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来

数位类统计问题--数位DP

有一类与数位有关的区间统计问题.这类问题往往具有比较浓厚的数学味道,无法暴力求解,需要在数位上进行递推等操作.这类问题往往需要一些预处理,这就用到了数位DP. 本文地址:http://www.cnblogs.com/archimedes/p/numerical-digit-dp.html,转载请注明源地址. 基础知识 [l,r] 意为 l<=且<=r的数 [l,r) 意为 l<=且< r的数 (l,r] 意为 l<且<=r的数 (l,r) 意为 l<且< r

数位dp(求1-n中数字1出现的个数)

题意:求1-n的n个数字中1出现的个数. 解法:数位dp,dp[pre][now][equa] 记录着第pre位为now,equa表示前边是否有降数字(即后边可不可以随意取,true为没降,true为已降):常规的记忆化搜索 代码: /****************************************************** * author:xiefubao *******************************************************/ #p