POJ 3286 How many 0's?(几多0?)

POJ 3286 How many 0's?(几多0?)

Time Limit: 1000MS   Memory Limit: 65536K

【Description】

【题目描述】

A Benedict monk No.16 writes down the decimal representations of all natural numbers between and including m and n, m ≤ n. How many 0's will he write down?

一个本尼迪克16号僧侣写下m与n之间并且包括mn的所有十进制自然数,m ≤ n。这之间他写下了多少个0?

【Input】

【输入】

Input consists of a sequence of lines. Each line contains two unsigned 32-bit integers m and n, m ≤ n. The last line of input has the value of m negative and this line should not be processed.

多组输入。每行有两个无符号32位整数m和n,m ≤ n。最后一行的m是负数,因此不该被执行。

【Output】

【输出】

For each line of input print one line of output with one integer number giving the number of 0's written down by the monk.

对于每行输入数据输出一行一个整数,表示这个僧侣写了多少个0.

【Sample Input - 输入样例】

【Sample Output - 输出样例】

10 11

100 200

0 500

1234567890 2345678901

0 4294967295

-1 -1

1

22

92

987654304

3825876150

【题解】

如果不考虑数据范围,这道题应该就是入门级别的数位DP(此处可参考HDU 2089)

不过这道的数据范围是unsigned int,无符号减法可能有问题,所以还是直接上int64吧。

这道题在具体的思路上基本一致,因为数据范围大了,就需要压缩一下空间,不能任性。

之后的计算方式大概可以变为:

计算0~N写了几个0,即个位上0写的次数,十位上0…,百位上的0…以此类推。

每一位按照其出现0的区间间隔与区间长度,计算这一位上写了几次0。

百位

[1000, 1099]

[2000, 2099]

[3000, 3099]

[4000, 4099]

……

十位

[100, 109]

[200, 209]

[300, 309]

[400, 409]

……

个位

[0, 0]

[10, 10]

[20, 20]

[30, 30]

……

以100为例:

  个位:直接100/10 +1 = 11(懒)。

  十位:100拆分成1 00

    其中1表示进位的次数,完整区间数1-0=1,剩余区间的元素个数 0–0+1=1。

    所以十位数的次数=0*10+1 = 1。

  百位:到达最高位,结束。

  结果:11+1 = 12。

以200为例:

  个位:20 0,200/10 +1 = 21。

  十位:2 10,完整区间数2-1=1,剩余区间的元素个数0-0+1=10。

    剩余10>9,所以超过9的元素最多只能提供10个0

    十位数的次数=1*10+10 = 11。

  百位:结束。

  结果:21+11 = 32。

其他数字的结果以此类推,就能计算0~N写了几个0。

【代码 C++】

 1 #include<cstdio>
 2 __int64 cmp[12];
 3 void rdy(){
 4     int i;
 5     for (cmp[0] = i = 1; i < 12; ++i) cmp[i] = cmp[i - 1] * 10;
 6 }
 7 __int64 calculate(__int64 now){
 8     if (now < 0) return 0;
 9     int i = 1;
10     __int64 right, left, opt = now / 10 + 1;
11     while (1){
12         left = now / (cmp[i + 1]);
13         right = now % (cmp[i + 1]);
14         if (right < now){
15             opt += (left - 1)*(cmp[i]);
16             if (right >= cmp[i]) opt += cmp[i];
17             else opt += right + 1;
18             ++i;
19         }
20         else break;
21     }
22     return opt;
23 }
24 int main(){
25     rdy();
26     __int64 a, b;
27     while (scanf("%I64d%I64d", &a, &b)){
28         if (a < 0) return 0;
29         printf("%I64d
", calculate(b) - calculate(a - 1));
30     }
31     return 0;
32 }
原文地址:https://www.cnblogs.com/Simon-X/p/5353491.html