统计数字问题
Time Limit: 1 Sec Memory Limit: 64 MBDescription
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1, 2,…,9。给定表示书的总页码的10 进制整数n (1≤n≤109) 。计算书的全部页码中分别用到多少次数字0,1,2,…,9。
Input
输入数据只有1 行,给出表示书的总页码的整数n。
Output
输出数据共有10行,在第k行输出页码中用到数字k-1 的次数,k=1,2,…,10。
Sample Input
11
Sample Output
1 4 1 1 1 1 1 1 1 1
我在网上看到这样的讲解,是这样说的:
比如,对于一个数字34567,我们可以这样来计算从1到34567之间所有数字中每个数字出现的次数: 从0到9999,这个区间的每个数字的出现次数可以使用原著中给出的递推公式,即每个数字出现4000次。从10000到19999,中间除去万位的1不算,又是一个从0000到9999的排列,这样的话,从0到34567之间的这样的区间共有3个。所以从00000到29999之间除万位外每个数字出现次数为3*4000次。然后再统计万位数字,每个区间长度为10000,所以0,1,2在万位上各出现10000次。而3则出现4567+1=4568次。之后,抛掉万位数字,对于4567,再使用上面的方法计算,一直计算到个位即可。首先考察由0,1,2...,9组成的所有n位数字(0~999...9,n个9)。从n个0到n个9共有10^n个n位数。在这10^n个n位数中,显然有对称性1,2,3...,9每个数字使用次数相同(0由于不能放在首位,所以要减去相应的个数)。设每个数字出现次数为f(n)则有如下递归式:
{ 10f(n-1) + 10^(n-1) n > 1 f(n) = | { 1 n = 1
可得:f(n) = n*10^(n-1)。(即:所有的n位数中:1,2,...,9出现的次数都为n*10^(n-1))。例如对n = 3时0~999中的1:1XX(100种选择),X1X(100种选择),XX1(100种选择)一共就是3*10^(3-1) = 300种选择。(注意:对于0:在此数值上减去10^(n-1)就行了)。所以最后可以由高位向低位统计(把0看成与其它一样的),最后再去相应的0的
个数就行了。
讲到这个地步了这个程序似乎也还不是很好写,我也给这些数字弄的挺乱的,举个例子好些吧....我们对1896采用递归来解决:先由0~999:每个加300。剩下的就是对
1000~1896的问题,其中对1每个加上897,在递归求解0~896就行了。这下完全理解我的精妙算法了吧。。。
---------------------------------------------------------------------------------------------------------------------------------*/
源程序(C语言实现):/*Counting.c *用tab1[10]表格来记录n*10^(n-1),这样就每次节省了时间了 *用tab2[10]表格来记录10^(n-1),计算0的时候用到 *结果存放在result中 */#include <stdio.h> #include <stdlib.h> #include <string.h>long num; long tab1[10] = {0, 1, 20, 300, 4000, 50000, 600000, 7000000, 80000000, 900000000}; long tab2[10] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; long result[10]; int flag = 0; //标记要不要减0,例如flag = 0,计算0~999,flag=1计算000~999
void compute (long x) { int len, i, high; long y; char string[10]; len = strlen (itoa(x, string, 10)); high = string[0] - 48; if (len == 1) { for (i = 0; i <= x; i++) { result[i] += tab1[len]; } if (flag == 0) { result[0] -= tab2[len]; } return; } else { for (i = 0; i <= 9; i++) { result[i] += tab1[len - 1]*high; } if (flag == 0) { for (i = 1; i < len; i++) { result[0] -= tab2[len - i]; } //flag = 1; } for (i = 0; i < high; i++) { result[i] += tab2[len]; } if (flag == 0) { result[0] -= tab2[len]; flag = 1; } for (i = 1; string[i] == 48 && len - i > 1; i++) //如果第二高位后面有几个0,都会丢失 result[0]++; y = x - high*tab2[len]; result[high] += (y + 1); compute (y); } }
int main () { int i; freopen ("count.in", "r", stdin); freopen ("count.out", "w", stdout); scanf ("%ld", &num); compute (num);
for (i = 0; i<10; i++) { printf ("%ld\n", result[i]); } return 0; }