题目如下:
一、初步思路
先按照题目,简单写下几组数字观察分析一下。

通过观察,我们发现:对于两位数而言,影响“1”的数目的因素主要表现在红色和蓝色标记的地方。
下面我们再写一些三位数的数字观察分析一下。

通过观察,我们发现:第五组灰色阴影标识的部分除了最高位,其他的和前面是重复的;对于101~199,最高位的“1”是不需要计算的,而除去最高位,剩下的部分则在二位数的情形中已经计算过了。对于201~999,最高位数字对于“1”的计算没有影响,因为它们不含“1”,而除去最高位的部分,则和二位数的情形一样。
二、特例研究
通过上面的分析,我们发现,“1”的数目和数字的位数是有关系的,和最高位的数字也是有关系的,另外,除去最高位部分,剩下的数字中“1”的个数的情形在前面已经出现过了。
另一方面,我们也发现,1~9,1~99, 1~999,1~9999……这些特殊范围中,数字“1”出现的次数是有规律的,总结一下就是:如果k = 999……999一共有m位,那么1~k中数字“1”出现的次数为bm= m * 10m-1。
现在我们继续使用上面几组数字(假设数字的位数是3):
1. 如果最高位是“1”,不妨设我们写下的数字是132,那么我们可以分为三部分来考虑:
(1)先算出1~99中数字“1”出现的次数;
(2)在100~132中,数字“1”在最高位共出现了33次;
(3)101~132除去最高位部分的数字中,数字“1”的个数和1~32中数字“1”出现的次数是一样的,我们可以利用递归来解决它。
2.如果最高位大于“1”,不妨设我们写下的数字是372,那么我们还可以分为三部分来考虑:
(1)在1~300中,“1~99”(以及“01~99”)共出现了3次;
(2)百位数字出现数字“1”的情形只在100~199中;
(3)在301~372除去最高位部分的数字中,数字“1”的个数和1~72中数字“1”出现的次数是一样的,我们可以利用递归来解决它。
三、公式提取
按照上述方式,我们再分析一下四位数的情形,就可以发现基本规律了。
设从1开始写,写到K,K的形式为:amam-1……a2a1
则有下述公式:
公式中,有关bm的定义请查看上一节。
四、算法实现
#include<math.h>
int count_number(int N)
{
if(0 == N) return 0;
if(N <= 9 && N >= 1) return 1;
int m = 0, h; //m用来存储位数,h用来存储最高位数字
int tmp = N;
while(tmp)
{
m++;
h = tmp % 10;
tmp = (int)(tmp / 10);
}
int tmp_1 = (m - 1) * pow(10, m - 2);
int tmp_2 = N - h * pow(10, m - 1);
if(1 == h) return (tmp_1 + tmp_2 + 1 + count_number(tmp_2));
return (h * tmp_1 + pow(10, m - 1) + count_number(tmp_2));
}
<—— 原创作品,转载请注明:http://blog.youkuaiyun.com/Phoenix_W_2010 ——>
——————————————————————————————————————————————————————
后记:
这是我在博客写的第一篇文章。我是尽量按照自己当时的思考过程来写的,也不知道这篇文章表述清楚了没有,如果大家读得很费劲,请多多包涵,O(∩_∩)O~。如果有什么错误,也请大家在回复中指出,我会修改的。
算法用到了递归,后来读了一下《编程之美》上面给出的方法,觉得自己水平还是差了很多啊,需要继续努力,O(∩_∩)O~。
好了,就写到这里吧。