今天去牛客网看了看 包含一 这道题,一开始没看清,以为它要统计 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'出现的总次数

相关文章: